GC(Garbage Collection)需要完成的三件事:
1.哪些内存需要回收?
已死对象所占内存需要回收。
2.什么时候回收?
内存不够用的时候回收。
垃圾回收分为Minor GC和(Major GC或者Full GC),即Minor GC为新生代垃圾回收,Full GC为老年代垃圾回收。
Minor GC触发条件,Full GC触发条件。对象会优先分配到Eden区(大对象会直接分配到老年代区域);大对象会直接进入老年代;Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。
3.如何回收?
垃圾回收分为Minor GC和(Major GC或者Full GC),即Minor GC为新生代垃圾回收,Full GC为老年代垃圾回收。堆将内存分为新生代、老年代、永久代三个区域。Minor GC就是针对新生代区域(复制算法),Full GC就是针对老年代区域(标记-整理),新生代的对象被垃圾收集器收集之后,其中已死对象被回收,还存活的对象增长到一定年龄(年龄计数器,经过一次GC年龄增长1)之后会晋升到老年代。
程序计数器、虚拟机栈、本地方法栈三个区域是随线程而生,随线程而灭,当方法或者活动结束的时候,他们也就自动回收内存了,所以不需要过多考虑回收的问题。java需要回收内存的地方主要就是说的堆,所以垃圾回收器关注的是堆内存的分配。
哪些内存需要回收就需要判断哪些对象需要回收。回收的对象即不可能再被任何途径使用的对象(已死对象)。
回收这些对象的方法:
(1)引用计数算法
即给每个对象添加一个引用计数器,当对象被引用时计数器加1,当引用失效时对象减1。但是该方法难以解决对象之间相互循环引用的问题。
(2)根搜索算法
通过一系列名为“GC Roots”对象作为起始点,从这些起始点开始往下搜索,搜索走过的路径就叫引用立链,当一个对象到GC Roots没有任何引用链的时候该对象就是不可用的。
那么哪些对象可以作为GC Roots?
▲ 虚拟机栈中引用的对象
▲ 方法区中的类静态属性引用的对象(例子?)——static
▲ 方法区中的常量引用的对象(例子?)——final
▲ 本地方法栈中JNI的引用对象
▲ 方法区中的类静态属性引用的对象(例子?)——static
▲ 方法区中的常量引用的对象(例子?)——final
▲ 本地方法栈中JNI的引用对象
以上两种方法的判断都设计到引用:强引用、软引用、弱引用、虚引用。
finalize()方法。
永久代的垃圾回收:废弃常量和无用类。——方法区?
垃圾回收算法
1.标记-清除算法:标记完之后统一回收被标记的对象。
缺点:空间碎片太多,当需要分配较大对象时无法找到足够的连续内存空间,导致提前触发一次垃圾收集。
![](https://img-blog.csdn.net/20160311171801341?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
2.复制算法:将内存分为两块,每次只使用其中的一块,当其中一块内存用完之后,将存活的对象复制到另外一块内存当中。
![](https://img-blog.csdn.net/20160311171820295?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
升级版:
Eden空间和两块较小的Survivor空间,默认大小为8:1;
3.标记-整理算法:在“标记-清除”算法的基础上,把后续步骤改为把存活对象都像一端移动,然后直接清理掉边界以外的内存。
![](https://img-blog.csdn.net/20160311171831827?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
4.分代收集算法:根据对象存活的周期将不同的内存划分为新生代、老年代。新生代才用复制算法,因为新生代每次垃圾收集会有大量对象死去,直接复制存活的对象效率高。老年代使用“标记-清理”或者“标记-整理”算法。
垃圾收集器
![](https://img-blog.csdn.net/20160311171849655?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
新生代的垃圾收集器:
Serial(单线程、复制算法)
ParNew(多线程、复制算法):Serial的多线程版本
Parallel Scavenge(多线程、复制算法)
Serial和ParNew收集器的侧重点在于尽可能地缩短垃圾收集时用户线程的停顿时间,因为他们在垃圾收集时必须暂停其他所有的工作线程(Stop the World)。Parallel Scavenge侧重的是吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))
老年代收集器;
Serial Old(单线程、标记-整理算法)
Parallel Old(多线程、标记-整理算法)
CMS(标记-清除算法)
新、老年代收集器:
G1(标记-整理):将新生代、老年代划分成多个大小固定的区域,然后根据优先级收集某一个区域,所以他可以在不牺牲吞吐量的情况下完成低停顿的垃圾回收。
内存分配原则:
(1)对象优先分配在Eden
(2)大对象直接进入老年代:通过-XX:PretenuringSiseThreshold参数设置(大于该参数的对象直接分配到老年代)
(3)长期存活的对象进入老年代:通过对象年龄计数器,超过一定岁数时晋升到老年代。通过-XX:MaxTenuringThreshold参数设置(设置晋升到老年代的年龄),如果Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到参数设置的值。
(4)空间分配担保:在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。
Java内存区域:
![](https://img-blog.csdn.net/20160311172038062?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)