哪些对象需要回收?
那些随着线程一起生死的对象不需要回收,程序计数器(pc register),虚拟机栈,本地方法栈,里面的变量不用回收。
动态分配的,堆里面的对象,不再被引用的对象需要被回收。
什么时候回收?
内存分配空间不足的时候会触发minor GC,major GC(full GC).
如何回收?(回收算法)
1.对象已死吗
1.1引用计数法
对象被引用则加1,引用失效则减1,这种方法效率高,但是很难解决对象之间循环引用的问题。
objA.instance = objB;
objB.instance =objA;
它们没有被其他对象引用,不会再被访问,但由于相互引用对方,导致它们的引用计数都不为0,于是GC无法回收它们。
1.2可达性分析算法 reachability analysis (Jvm 采用这种方法判断对象是否可以被回收)
当一个对象到GC Roots没有任何引用链相连,则对象不可用。
一个对象死亡,至少要经历两次标记过程。
在堆中,尤其新生代,一次垃圾回收一般可以回收70%-95%,永久代的垃圾回收效率远低于此。
无用的类:
1.该类的所有实例都已经被回收,就是Java堆中不存在该类的任何实例。
2.加载该类的classLoader已经被回收。
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类的方法。
2.垃圾回收算法
2.1标记-清除算法
标记不可达对象,然后清除。
不足:标记清除的两个过程效率都不高,标记清除之后产生大量不连续的内存碎片,空间碎片太多会导致在需要
分配大对象时,无法找到足够的连续的内存,从而触发GC。
2.2复制回收算法
将内存分为大小相等的两块,每次只使用其中的一块,当这块内存用完了,就将还活着的对象复制到另一块上面,
然后已使用的内存一次清理,每次对整个半区进行内存回收。效率很高。但是这种算法,只使用了一半的内存。
内存空间使用率很低。
IBM对发现大部分新生对象都符合朝生夕灭的特点,对复制回收算法做改良
复制回收算法使用在新生代
eden:from survivor:to survivor = 8:1:1
每次使用eden区和其中一块from survivor,回收时,将eden和s1中还活着的对象一次移到to survivor,每次
新生代清理90%空间,只有10%会被浪费。当to survivor不够用时,会需要老年代进行分配担保。
2.3标记整理算法
当对象存活率较高时,复制效率低,而且需要额外的空间担保。
标记-整理算法,标记完后,把存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
老年代使用标记整理算法
分代回收虚拟机,在新生代使用复制回收算法,在老年代使用标记整理算法。
GC进行时必须停顿所有Java执行线程。stop-the-world.
2.3内存分配与回收策略
1.对象优先在eden分配,当eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC
-XX:+PrintGCDetail , -Xms20M 堆初始内存 , -Xmx100M 堆最大内存 , -Xmn20M 新生代内存
-XX:SurvivorRatio=8 ,意思是:eden:s1:s2 = 8:1:1
新生代GC(Minor GC),指发生在新生代的垃圾回收,Minor GC非常频繁,回收速度也比较快。
老年代GC(Major GC/Full GC),发生在老年代的GC,Major GC速度一般比Minor GC慢10倍以上。
2.大对象直接进入老年代
典型的大对象:字符串,数组
-XX:PretenureSizeThreshold,大于这个设置值的对象直接在老年代分配。避免新生代发生大量的内存复制。
3.长期存活的对象将进入老年代
对象年龄age,每经过一次minor GC,年龄就加1,增加到一定程度(默认15岁),就会被晋升到老年代。
老年代年龄阈值,-XX:MaxTenuringThreshold
4.动态对象年龄判定
在survivor空间中,相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于或者等于该年龄就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。
5.空间分配担保
在发生Minor GC 之前虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,成立则Minor GC是无风险的。风险可能要进行一次Full GC。
参考文献:JVM高级特性与最佳实践(第二版)