JAVA中的GC机制详解 (一)

来源:互联网 发布:天刀男性捏脸数据导入 编辑:程序博客网 时间:2024/06/05 22:32

java内存运行时的程序计数器、虚拟机栈和本地方法区随线程而生,随线程而灭,这些区域的内存分配和回收都具有确定性,但在java堆和方法区中只有程序处于运行区时才知道创建哪些对象,这部分的内存分配和回收是动态的


回收时间

eden满了minor gc,对新生代空间清理,升到老年代的对象大于老年代剩余空间full gc,老年代清理,或者小于时被HandlePromotionFailure参数强制full gc;gc与非gc时间耗时超过了GCTimeRatio的限制引发OOM

回收什么

在java堆里几乎存放着所有的对象实例,gc在对堆进行回收时,需要判断对象是否存活,如何判断对象是否存活呢?可以通过一个计数器,当该对象被引用时,计数器加一,引用完毕时减一,gc直接回收计数器为零的实例,该方法虽然简单有效但是无法解决循环引用的问题,所以,现在jvm中采用可达性分析算法,该方法的基本思路是以一系列的“GC roots”对象,向下的节点作为一条引用链,当一个对象没有向上到gc roots的引用链时,则判定为可回收的对象。

此外,即使在可达性分析中不可达的对象,也并非非死不可,要真正宣告一个对象死亡,至少要经过两次标记过程:判定不可达后,将会进行第一次标记和筛选,条件是是否有必要执行finalize(),当对象没有覆盖finalize或者已经执行过,就没有必要执行直接回收。如果有必要执行虚拟机就把他放在一个F-queue中,由一个低线程执行,稍后进行第二次执行,如果对象能在finalize中重新与引用链建立连接,那它就被移除。

所以,回收的是不可达并标记一次的对象

如何回收

一、具体回收方法每种收集器都各有异同,几种方法论如下:

标记—清除算法:分为标记清除两个阶段,首先标记出所有需要回收的内存,然后统一回收,标记过程见上,后面的几种算法都是在此算法的基础上对其不足进行改进,他的不足体现在效率低,标记和清除都是效率不高的过程,二是空间,清除过后会产生大量的内存碎片,导致内存分配较大的对象时找不到足够的连续空间时,不得不触发另一次垃圾收集


复制算法:将内存分为大小相等的两块,每次使用其中一块,当这一块使用完了,将存活的对象复制到另外一块,一次性清理掉原来空间,实现简单,运行高效,但是,内存直接缩小一半的代价较大,实际上,现在的商业虚拟机都采用这方法来回收新生代,新生代对象98%都是朝生夕死的,所以并不需要按照1:1的比例分配内存空间,只需分为一个较大的Eden和两个较小的Survivor,每次使用Eden和其中一块Survivor,完毕后将存活的对象复制到另外一块Survivor,内存使用率为90%,当然不是每次都不多于10%存活,多出来的有老年区内存担保


标记—整理:在对象存活率较高的老年代,复制算法效率极低,有人根据老年代的特点提出了标记整理算法,标记过程和标记清除算法一样,但后续让所有存活对象向一端移动,在清理掉端边界外的内存


分代,将内存按照生存周期的不同分为新生代和老年代,根据每个年代的特点选用最恰当的算法




0 0
原创粉丝点击