首页 > 代码库 > 垃圾回收

垃圾回收

如何判断一个对象是可回收的?
    Java虚拟机采用可达性分析算法来判断对象是否存活。算法基本思想:通过一系列称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索锁走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,证明此对象是不可用的,将会被判定为可回收对象。
    可作为GC Roots的对象:
        虚拟机栈(栈帧中的局部变量表)中引用的对象
        方法区中类静态属性引用的对象
        方法区中常量引用的对象
        本地方法栈中JNI(native方法)引用的对象

强引用、软引用、弱引用、虚引用
    强引用(Strong Reference):类似Object obj = new Object)的引用,存在强引用的对象,永远不会被GC。
    软引用(Soft Reference):还有用但是非必须的对象。只存在软引用的对象,只有在内存不足时才被GC。可用来实现缓存(还没有被回收,直接获取对象),JDK中java.lang.ref.SoftReference用来实现软引用
    弱引用(Weak Reference):具有弱引用的对象具有更短的生命周期。只具有弱引用的对象,在垃圾收集器时会立即被回收,不管内存是否充足。JDK中java.lang.ref.WeakReference用来实现弱引用
    虚引用(Phantom Reference):一个对象是否有虚引用的存在不会影响对象的生命周期,也无法通过虚引用来获取一个对象的实例。唯一的用处:能在对象被GC时收到系统通知。JDK中java.lang.ref.PhantomReference用来实现虚引用

垃圾收集算法
    标记-清除算法:先标记所有要回收的对象,标记完成后统一回收。不足之处:标记清除效率较低;标记清除后会产生大量不连续的内存碎片,在后续为较大对象分配内存时,无法找到足够的连续内存而不得不再触发一次GC。
    复制算法:将堆内存分为新生代和老年代,新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor空间。当回收时,将Eden和Survivor中还存活着的对象一次性复制到另一块Survivor空间上,最后清理掉Eden和使用过的Survivor空间。解决了内存碎片问题。不足之处:在对象存活率较高时要进行较多的复制操作,效率会变低,如果第二块Survivor没有足够空间存放,需要依赖于其他内存(老年代)。
    标记-整理算法:先标记所有要回收的对象,然后让存活的对象向一端移动,最后清理端边界意外的内存。
    分代收集算法:对新生代和老年代采用不同的垃圾收集算法。新生代中每次垃圾收集时都发现有大量对象死去,只有少量对象存活,采用复制算法。老年代中对象存活率高,且没有额外空间进行分配担保,采用标记-清除算法或标记-整理算法。

垃圾收集器
    Serial收集器:是一个单线程收集器,在进行垃圾回收时,必须暂停其他所有的工作线程,直到收集结束。试用于单个CPU的环境,由于没有线程交互的开销,收集效率高。(适用于在Client模式下运行的虚拟机)
    ParNew收集器:是Serial收集器的多线程版本。是运行在Server模式下的虚拟机首选的新生代收集器,原因是除了Serial收集器外,目前只有它能与CMS收集器配合工作。
    Parallel Scavenge收集器:是一个新生代收集器,使用复制算法。主要用于控制垃圾收集的吞吐量,可以通过参数精确的控制吞吐量,分别是控制最大垃圾收集停顿时间和吞吐量大小。常被称为“吞吐量优先”收集器。
    Serial Old收集器:是serial收集器的老年代版本,也是一个单线程收集器,使用标记-整理算法。此收集器同Serial收集器一样,主要用于给Client模式下的虚拟机使用,如果在Server模式下使用,主要有两大用途:一是在JDK1.5之前与Parallel Scavenge收集器搭配使用;二是作为CMS收集器的后备方案,在并发收集器发生Concurrent Mode Failure时使用。
    Parallel Old收集器:是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。在JDK1.6之后可以和Parallel Scavenge收集器组合使用。
    CMS收集器:(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的收集器,是基于标记 -清除算法实现的。运作过程分为4个步骤:
        初始标记:标记GC Roots能直接关联到的对象,速度很快。
        并发标记:进行GC Roots Tracing。即从GC Toots向下搜索并标记没有引用链的对象。
        重新标记:修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。此阶段停顿时间会比初始标记阶段稍长,但远比并发标记的时间短。
        并发清除:清除对象的过程。
        其中初始标记和重新标记仍要暂停所有线程。整个过程中耗时最长的并发标记和并发清除过程收集器 线程都可以和用户线程一起工作,从整体上来说CMS收集器的内存回收过程与用户线程是并发执行的。
        CSM收集器的缺点:
            1、CMS收集器对CPU资源非常敏感。CMS默认启动的垃圾收集器线程数是(CPU数量+3)/4,当CPU数量较少时,垃圾收集器会占用大量CPU资源,导致用户线程执行速度降低,应用程序吞吐量降低。
            2、CMS收集器无法处理浮动垃圾(CMS在并发清除阶段用户线程仍在运行,可能会产生新的垃圾,只能等待下一次GC时处理)。CMS收集器需要预留足够的内存给用户线程使用,因此不能等到老年代几乎满了再进行收集。JDK1.6之后,CMS收集器启动阈值设置为92%,即老年代使用了92%之后就启动CMS收集器。当CMS运行期间预留的内存无法满足程序的需要时,会出现“Concurrent Mode Failure”失败,这时会启用后备方案:临时启动Serial Old收集器来进行老年代垃圾回收。
            3、CMS是基于标记-清除算法实现的,收集结束会产生大量空间碎片,可能会出现老年代还有大量空间剩余,但是无法找到足够大连续空间来为新对象分配内存,而不得不再触发一次Full GC。CMS提供了一个整理碎片的参数,默认开启,用于在CMS需要在Full GC时开启内存碎片的合并整理过程,内存整理过程无法并发执行,导致用户线程停顿时间变长。
    
    G1收集器

GC的过程,GC对程序有什么影响?当发现虚拟机频繁GC时应该怎么办?
    GC进行时必须停顿所有Java执行线程(Stop the world)

垃圾回收