Java内存管理分为内存分配和内存回收,区别于C++的“谁污染谁治理”原则,java的内存回收完全有GC负责。以下简要分析GC工作原理:
1、垃圾内存的判定:
a)应用计数:早起采用的方式,对每个对象的应用个数计数,当应用计数为0的时候判断该对象为垃圾对象;
b)应用遍历:将对象和应用看成是定点,指向对象的引用当成有向边,组成有向图。图中对象分为三种状态,分别是可到达状态,可恢复状态,和不可代打状态。
1)可达状态:在一个对象创建后,有一个以上的引用变量引用它。在有向图中可以从起始顶点导航到该对象,那它就处于可达状态。
2)可恢复状态:如果程序中某个对象不再有任何的引用变量引用它,它将先进入可恢复状态,此时从有向图的起始顶点不能再导航到该对象。在这个状态下,系统的垃圾回收机制准备回收该对象的所占用的内存,在回收之前,系统会调用finalize()方法进行资源清理,如果资源整理后重新让一个以上引用变量引用该对象,则这个对象会再次变为可达状态;否则就会进入不可达状态。
3)不可达状态:当对象的所有关联都被切断,且系统调用finalize()方法进行资源清理后依旧没有使该对象变为可达状态,则这个对象将永久性失去引用并且变成不可达状态,系统才会真正的去回收该对象所占用的资源。
2、垃圾回收机制
串行回收和并行回收:区分是否有多个cpu参与垃圾回收,并行回收的执行效率高,但也会带来内存碎片。
并发回收和应用程序停止:不采用并发执行的内存回收方式会停止正在运行的应用程序,而并发执行不需要停止应用,但是会带来更大的开销,需要更多的堆内存。
压缩与不压缩:不压缩回收方式中垃圾回收器只负责回收内存垃圾,不对内存进行整理压缩,会产生内存碎片,压缩回收方式在回收玩内存之后增加一个整理压缩内存的过程。
复制回收:将堆内存分为两块,新创建的对象放在A区,垃圾回收时对B去进行遍历。将非垃圾对象搬迁到A区,统一清除B区,然后将A,B区反之操作,复制回收的优点在于遍历的开销少,缺点在于复制开销大,增加堆内存消耗。适用于生命周期短的对象。
分代回收:“将内存分为多个区域,区分标准为对象的生命周期长短。对每个区域内的垃圾回收采取不同的回收机制
Young代:对象生命周期短,采用复制回收机制。新创建的对象统一被分配到Young代,回收频率高
Old代:Young代中生命周期长的对象被转入Old代,该区域中采用标记压缩回收方法
Permanent代:用来装载Class,方法等信息,默认为64M,不会被回收
3常见的垃圾回收器
在此之前,我们先讲一下下面将会涉及到的并发和并行两个词的解释:
1)并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;
2)并发:指用户线程与 垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序继续执行,而垃圾收集程序运行于另一个CPU上。
好啦,继续讲垃圾回收器:
1)串行回收器(只使用一个CPU):Young代采用串行复制算法;Old代使用串行标记压缩算法(三个阶段:标记mark—清除sweep—压缩compact),回收期间程序会产生暂停,
2)并行回收器:对Young代采用的算法和串行回收器一样,只是增加了多CPU并行处理; 对Old代的处理和串行回收器完全一样,依旧是单线程。
3)并行压缩回收器:对Young代处理采用与并行回收器完全一样的算法;只是对Old代采用了不同的算法,其实就是划分不同的区域,然后进行标记压缩算法:
① 将Old代划分成几个固定区域;
② mark阶段(多线程并行),标记可达对象;
③ summary阶段(串行执行),从最左边开始检验知道找到某个达到数值(可达对象密度小)的区域时,此区域及其右边区域进行压缩回收,其左端为密集区域
④ compact阶段(多线程并行),识别出需要装填的区域,多线程并行的把数据复制到这些区域中。经此过程后,Old代一端密集存在大量活动对象,另一端则存在大块空间。
4)并发标识—清理回收(CMS):对Young代处理采用与并行回收器完全一样的算法;只是对Old代采用了不同的算法,但归根待地还是标记清理算法:
① 初始标识(程序暂停):标记被直接引用的对象(一级对象);
② 并发标识(程序运行):通过一级对象寻找其他可达对象;
③ 再标记(程序暂停):多线程并行的重新标记之前可能因为并发而漏掉的对象(简单的说就是防遗漏)
④ 并发清理(程序运行)