十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
gc是指垃圾回收机制,当一个对象不能再被后续程序所引用到时,这个对象所占用的内存空间就没有存在的意义了,java虚拟机会不定时的去检测内存中这样的对象,然后回收这块内存空间。
创新互联建站 - 西部信息中心,四川服务器租用,成都服务器租用,四川网通托管,绵阳服务器托管,德阳服务器托管,遂宁服务器托管,绵阳服务器托管,四川云主机,成都云主机,西南云主机,西部信息中心,西南服务器托管,四川/成都大带宽,成都机柜租用,四川老牌IDC服务商
GC的基本原理:
对于程序员来说,用new关键字即在堆中分配了内存,我们称之为“可达”。对于GC来说,只要所有被引用的对象为null时,我们称之为“不可达”,就将进行内存的回收。
当一个对象被创建时,GC开始监控这个对象的大小、内存地址及使用情况。GC采用有向图的方式记录和管理堆(heap)中的所有对象,通过这种方式可以明确哪些对象是可达的,哪些不是。当确定为不可达时,则对其进行回收。
保证GC在不同平台的实现问题,java规范对其很多行为没有进行严格的规定。对于采用什么算法,什么时候进行回收等。
GC是垃圾回收的意思(gabage collection),内存处理器是编程人员容易出现问题的地方,忘记或者错误的内存回收导致程序或者系统的不稳定甚至崩溃,java的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,java语言没有提供释放已分配内存的俄显示操作方法。
希望能帮到你,谢谢!
IBM JVM的GC分为三个步骤 Mark phase(标记) Sweep phase(清扫) Compaction phase(内存紧缩) 在了解这些过程之前 我们先看一下IBMJava中的对象的Layout和Heap lay out 一个Java对象在IBM vm中的结构如下 size+flags mptr locknflags objectdata size+flags 这是一个 byte的slot( 平台) 这个slot的主要功能就是描述对象的尺寸 由于IBMJava中的对象都是以 byte的倍数分配的 因此对象的尺寸其实就是真实尺寸/ 存放在 byte的slot中 另外在这个slot的低三位是保留字段起到标记对象的作用 他们分别为 bit :swapped bit 这个交换位被用于Compaction phase即内存紧缩阶段使用 同时 这一位在标记堆栈溢出的时候(mark stack overflow)也被用于标记NotYetScanned状态 bit dosed bit 这个位用于标示这个对象是否被某个堆栈或者寄存器reference到了 如果这个标志被至位则这个对象就不能在当前的GC cycle中被删除 而且如果某个reference指向的内存不是一个真实的reference比如是一个简单的float 或者integer变量但是它的值恰巧就是Heap中某个Object的地址的时候 我们就不能修改这个refernece 这种对象的bit 也被置为 bit :pinned bit 标记一个对象是否是一个一个钉扣对象(PINNED object) 一个Pinned Object也不能被GC删除 因为他们可能在Heap之外被reference到了 典型的一个例子就是Thread 还记得我上面说的僵死县城么?它不能被删除的道理就是这个 另外一种PinnedObject就是 JNI Object 即被本地代码使用的对象 Mptr: 在 平台上也是 byte的slot Mptr有两个功能 如果mptr不是一个数组 则Mptr指向一个方法块(method block) 你可以通过这个method block来得到一个类块(class block) 这个类块 告诉你这个Object是属于哪个class的实例 method block和class block由Class Loader分配 而不是heap在heap中进行分配 如果mptr是一个数组(Array) mptr包含了这个对象中 数组的元素个数 lockflags 在 平台上也是 byte的slot 但是这个slot只有低 位被用到 bit :是array flag 如果这个位被置位 那么这个对象就是一个数组同时mptr字段就包含了数组的元素个数 bit 是hashed和moved bit 如果这个位被置位 那么他就告诉我们这个对象在被hashed以后被删除了 Object Data 就是这个对象本身的数据 Heap layout: heap top heap limit heap base heap base是heap的起始地址 heap top是heap的结束地址 heaplimit 是当前程序使用的那段heap可以进行扩展和收缩的极限 你可以用 Xmx参数在java运行的时候对heap top和heap base进行控制 Alloc bits 和 mark bits heap top allocmax markemax heap limit alloc size marksize heap base 上面这个结构描述了heap和alloc bits 以及 markbits之间的关系 allocbits和markbits都是元素为 个bit的vector 他们与heap有同样的长度 下面是两个对象被分配以后在heap和两个vector中的表现 heaptop allocmax markmax heaplimit allocsize marksize object top object base object allocbit object markbit object top object base object allocbit 如上面的结构 如果一个对象在heap被alloc出来 那么在allocbits中就标示出这个对象的起始地址所在的地址 allocbits中只标记起始地址 但是这个过程告诉我们这个对象在那里被创建 但是不告诉我们这个对象是否存活 当在mark phase中如果某一个对象比如object 仍然存活 那么就在markbits中对应的地址上标记一下The free list IBM jvm中的空闲块用用一个free list链标示 如图 freechunck freechunck freechunckn size size size next next next NULL freeStorage freeStorage freeste 有了这些基本概念我们来看看Mark phase的工作情况 MarkPhase GC的Mark phase将标记所有还活着的对象 这个标记所有可达对象的过程称为tracing Jvm的活动状态(active state)是由下面几个部分组成的 每个线程的保存寄存器(saved registers) 描述线程的堆栈 Java类中的静态元素 以及局部和全局的JNI(Java Native Interface)引用 在Jvm中的方法调用都在C Stack上引发一个Frame 这个Frame包含了 对象实例 为局部变量的assignment结果或者传入方法的参数 所有这些引用在Tracing过程中都被同等对待 实际上 我们可以把一个线程的堆栈看城一系列 bytes slot的集合 然后对每一个堆栈都从顶向下对这些slot进行扫描 在扫描的过程中都必须校验每个slot是否指向heap当中的一个真实的对象 因为在前面我就说过 很有可能这些slot值仅仅是一个int或float但是他们的值恰巧就等于heap中的一个对象地址 因此在扫描的时候必须相当的保守 扫描的时候必须保证所有的指针都是一个对象 而且这个对象没有在GC中被删除 只有符合下面条件的slot才是一个指向对象的指针 必须以 byte的倍数分配的内存 必须在heap的范围之内(即大于heapbase小于heaptop) 对应的allocbit必须置为 满足这些条件的对象引用我们称为roots 并且把他们的dosed bit置为 表示不能被GC删除 我想大家已经知道C#中为何连Int和Float都是OBject的原因了吧 在C#中因为都是OBject因此 在tracing的过程中就减少了一次校验 这个减少对性能起到很大的影响 如果扫描完成 那么Tracing过程便能安全精确的执行 也就是说我们可以在roots中通过reference找到他对应的objects 由于他们是真实的reference 那么我们就能够在pactionphase中移动对应的对象并且修改这些reference Trace过程使用了一个可以容纳 k的slots的stack 所有的引用逐个push进入这个堆栈并且同时在markbits中进行标记 当push和mark的工作完成之后 我们开始pop出这些slot并且进行trace 常规的对象(非数组对象)将通过mptr去访问clas *** lock clas *** lock将会告诉我们从这个对象中找到的其他对象的reference在那里?当我们在clas *** lock找到一个refernce以后 如果发现他没有被mark 那么我们就在markallocbits中mark他然后把他再压入堆栈 数组对象利用mptr去访问每个数组元素 如果他们没有mark则mark然后压入堆栈 Trace过程一直持续进行 直到堆栈为空 MarkStack OverFlow 由于markStack限制了尺寸 因此它可能会溢出 如果溢出发生 那么我们就设定一个全局的标志来表明发生了MarkStack OverFlow 然后我们将那些不能push入stack的OBject的bit 设定为NotYetScanned 然后当tracing过程完成以后 检验全局标志如果发现有overflow则把NotYetScanned的对象再次压入堆栈开始新的tracing过程 并行Mark(Parallel Mark) 由于使用逐位清扫(biise sweep)和内存紧缩规避功能 GC将化大部分的时间是用于Mark而非前面两项 这就导致了IBM JVM需要开发一个GC的并行版本 并行GC的目的不是以牺牲单CPU系统上的效能来换取在 路对称CPU系统上的高效率 并行Mark的基本思想就是通过多个辅助线程(helper thread)和一个共享工作的工具来减少Marking的时间 在单CPU系统中 执行GC工作的只有一个主线程 Parallel mark仍然需要这个主线程的参与 他充当了管理协调的角色 这个Thread所要执行的工作和单CPU上的一样多 包括他必须扫描C Stack来鉴别需要收集的roots指针 一个有N路对称CPU的系统自动含有n 个helper thread并且平均分布在每个CPU上 master thread将scan完的reference集合进行分块 然后交给helper thread独立完成mark工作 每个Helper thread都被分配了一个独立的本地mark stack 以及一个shareable queue sharqueue将存放help thread在mark overflow的时候的NotyetScanned对象 然后由master thread将sharequeue中的对象balance到其他已经空闲的thread上去 并发Mark(Concurrent mark) Concurrent mark的主要目的在于当heap增长的时候减少GC的pause time 只要heap到达heap limit的时候 Concurrent mark就会被执行 在Concurrent phase中 GC要求应用中的每个线程(不是指helper thread而是应用程序自己开启的线程以便充分利用系统资源)扫描他们自己的堆栈来得到roots 然后使用这些roots来同步的trace 可达对象 Tracing工作是由一个后台的低优先级的线程执行 同时程序自己开启的线程在分配内存的时候必须执行heap lock allocation 由于使用程序自己开启的线程并发的执行mark live objects 我们必须纪录那些已经trace过的object的变化 这个功能是采用一个叫写闸(write barrier) 来实现的 这个写闸在每次改变引用的时候被激活 它告诉我们什么时候一个对象被跟新过了 以便我们从新扫描那部分heap 写闸的具体实现是Heap会分配出 byte的内存段每个段都分配了一个byte在卡表中(card table) 无论何时一个对象的reference被更新cardtable将同步纪录这个对象的起始地址 使用Byte而不用bit的原因是写byte要比写bit快 倍 而且我们可能希望空余的bit会在未来被用到 当Concurrent mark执行完毕以后 S collection(stop total world)将会被执行 s的意思是指suspend所有程序自己开启的线程 因此我们可以看到如果使用Concurrent mark那 lishixinzhi/Article/program/Java/JSP/201311/19555
System.gc()用来强制立即回收垃圾,即释放内存。
java对内存的释放采取的垃圾自动回收机制,在编程的时候不用考虑变量不用时释放内存,java虚拟机可以自动判断出并收集到垃圾,但一般不会立即释放它们的内存空间,当然也可以在程序中使用System.gc()来强制垃圾回收,但是要注意的是,系统并不保证会立即进行释放内存
就是垃圾回收。
在虚拟机中将对象分为新生代,旧生代和永生代,使用不同的算法进行回收。