第三章 垃圾收集器与内存分配策略被引用
本章主要讲解JVM动态内存回收的原理、算法,各个不同的垃圾收集器对原理和算法的实现, 以及对象实例在堆内存究竟是如何分配的。
3.1 概述
垃圾收集主要要解决如下3个问题:
1、哪些对象需要被回收?
2、如何回收?
3、何时回收?
3.2 对象已死吗?
为了确定对象是否可以被回收,有2种思想
1、引用计数算法:引用计数是指对象自身维护一个引用计数器,每当被引用一次,计数器加一。引用失效,计数器减一。那么当进行GC时,引用计数器为0的对象就可以被认为是可以回收的。
这种算法现在已经基本上不用了,因为它解决不了对象间相互循环引用的问题。
2、可达性分析算法:该算法在判断对象是否可以被回收时,是通过一个叫做GC ROOTS的东西作为起点,向下进行搜索,形成一个引用链,如果一个对象在引用链上,说明是存活的。如果不在,这说明可以被回收。
GC ROOTS由以下对象组成: 方法局部变量中引用的对象、类静态属性引用的对象、常量引用的对象等。
目前的JVM基本都采用此算法。
引用类型
除了一般的引用(强引用)外,JAVA中还有以下3中引用类型
1、软引用:被软引用关联的对象,在一般GC时不会被回收,除非JVM发现要发生OOM了,这时候才会对这些对象进行回收,回收后如果空间还不够,才会发生OOM。SoftReference
2、弱引用:被弱引用关联的对象,无论内存空间是否足够,都会在GC时被回收。WeakReference。
3、虚引用:最弱的引用,虚引用不影响内存回收,也不能通过虚引用取得对象实例。PhantomReference。
finalize方法
对象被标记为可以回收时,并不会立即回收,而是有如下逻辑:
是否覆盖了finalize方法?没有则会被回收。
finalize方法是否已经执行过一次?是的话则会被回收。
finalize方法中是否为该对象重新关联了引用?没有则会被回收。
3.3垃圾收集算法
1、标记-清理算法:先对无用对象进行标记,然后再回收这些对象。
优点:简单。
缺点:经过标记-清理算法回收后,内存中会出现不连续的区域。
2、复制算法:将内存分为2部分,每次只使用其中的一部分,在GC时,将存活对象复制到另一边,再清理掉当前部分。
优点:内存中不会出现不连续的区域。
缺点:内存使用率低。
3、标记-整理算法:先对无用对象进行标记,然后再回收这些对象。并且将存活对象统一放置到内存的一侧。
现代JVM一般都会把堆内存分为新生代和老年代。新生代中由于对象存活时间很短,因此使用复制算法。老年代则一般使用标记--清理或者标记-整理算法。
3.5 垃圾收集器
1、Serial New
新生代的收集器,使用复制算法。是一个单线程的GC,在标记对象和清理对象时,都会暂停其他线程。
2、Serial Old
老年代的收集器,使用标记清理算法。是一个单线程的GC,在标记对象和清理对象时,都会暂停其他线程。
3、ParNew
新生代收集器,使用复制算法,可以看做是SerialNew的多线程版。
4、ParOld
老年代收集器,使用标记-整理算法,可以看做是SerialOld的多线程版。
5、Paraller Scavenge
新生代收集器,采用复制算法。多线程收集器,可以控制吞吐量(执行业务代码的时间/(执行业务代码时间+垃圾收集时间))。在标记对象和清理对象时,都会暂停其他线程。
6、CMS收集器
老年代收集器,采用标记-清理算法。多线程。可以在一定程度上实现垃圾收集和业务代码并发执行。
CMS垃圾收集步骤如下:
初始标记:从GC ROOTS能直接关联的对象。暂停其他线程。
并发标记:找出不能被GC ROOTS关联的对象。和业务线程并发运行。
重新标记:修正在并发标记时发生变动的引用。暂停其他线程。
并发清除:和业务线程并发运行。
CMS缺点:
会产生空间碎片。
由于在清理对象时,业务代码还在执行,因此不能等到内存快占满了再清理,要给业务代码执行留有一定的空间。这个空间一般为老年代的8%。如果该空间不够,则会临时启动Serial Old来执行FULL GC。
7、G1收集器
G1可以用于整个堆内存,它没有将堆内存分为新生代和老年代,而是将堆分为一个个大小相等的独立区域Region。可与用户线程并发执行。
3.6 内存分配与回收策略
新生代内存默认情况下的分配:Eden:Survivor1:Survivor2 = 8 : 1 : 1
1、对象默认分配在新生代的Eden区,如果开启了本地线程分配缓冲,则优先在TLAB上分配。
2、当Eden区空间不够时,会发生Minor GC,Minor GC时如果存活的对象大于Survivor空间的容量,则这些对象就直接进入老年代。
3、如果设置了-XX:PretenureSizeThreshold参数,那么大于该参数的大对象则会直接进入老年代。
4、如果在Survivor空间中,相同年龄所有对象的大小超过Survivor空间的一半,那么该年龄及以上的对象直接进入老年代。
5、如果新生代中的对象经过了15次Minor GC后仍然存活,那么会进入老年代,15这个数字可以通过-XX:MaxTenuringThreshold来设置。
6、在发生Minor GC前,会先判断老年代连续空间是否大于新生代对象总大小或者历次从新生代进入老年代对象的平均大小。如果大于则进行Minor GC,否则进行Full GC