Java垃圾回收机制

JVM框架

Java虚拟机HotSpot的框架:

Java垃圾回收机制

JVM主要组成部分:Class Loader(类加载器)、Runtime Data Area(运行时数据区)、执行引擎(Execution Engine)。

JVM垃圾回收简介

Step 1: Marking

Java垃圾回收机制 

GC算法在扫描存活对象时通常需要从Root节点开始,扫描所有存活对象的引用,构建出对象图。

Root:静态字段、方法参数、局部变量、CPU寄存器

Step 2: Normal Deletion

Java垃圾回收机制 

删除没有被引用的对象,释放空间。

Step 2a: Deletion with Compacting

Java垃圾回收机制

删除垃圾并压缩存活的引用对象,有利于提高内存分配的效率。

Generational Garbage Collection(分代垃圾回收)

HotSpot堆结构:

Java垃圾回收机制 

Young Generation:从eden区分配新对象,eden区满后,发生一次minor garbage collection,把eden区和一个survivor区中存活的对象移动到另一个survivor区中,存活的对象age加1,当存活的对象age达到一个阈值时晋升到Old Generation。

Old Generation:保存存活长久对象的地方,Old Generation满后会发生major garbage collection(full garbage collection)。

Stop the World Event:minor garbage collection和major garbage collection都是Stop the World Event,即垃圾回收的时候会暂停程序中线程的执行。

Permanent generation:保存JVM中用于描述类和方法的元数据信息。

整个GC的流程总结图:

Java垃圾回收机制 

GC分代的基本假设是:绝大部分对象的生命周期都非常短暂,存活时间短。

分配小对象的开销负担小,不要吝啬去创建。

GC最喜欢这种小而短命的对象。

让对象的生命周期尽可能短,例如在方法体内创建,使其能尽快地在YoungGC中被回收,不会晋升(romote)到年老代(Old Generation)。

对象分配的优化:尽量避免大对象的分配,当对象大到Eden Generation放不下时,JVM只能尝试去Old Generation分配,这种情况需要尽可能避免,因为一旦在Old Generation分配,这个对象就只能被Old Generation的GC或是FullGC回收了。

不可变对象可以减少GC的压力:Hotspot JVM为了提高YoungGC的性能,避免每次YoungGC都扫描Old Generation中的对象引用,采用了卡表(Card Table) 的方式。简单来说,当Old Generation中的对象发生对Young Generation中的对象产生新的引用关系或释放引用时,都会在卡表中响应的标记上标记为脏(dirty),而YoungGC时,只需要扫描这些dirty的项就可以了。可变对象对其它对象的引用关系可能会频繁变化,并且有可能在运行过程中持有越来越多的引用,特别是容器。这些都会导致对应的卡表项被频繁标记为dirty。而不可变对象的引用关系非常稳定,在扫描卡表时就不会扫到它们对应的项了。

指定容器初始化大小可以减少GC的压力:每次容器扩容分配更大的空间,可能会增加GC的次数。

各类引用:java.lang.ref.Reference有几个子类,用于处理和GC相关的引用。JVM的引用类型简单来说有几种:

Strong Reference,最常见的引用。

Weak Reference,当没有指向它的强引用时会被GC回收。

Soft Reference,只当临近OOM时才会被GC回收。

Phantom Reference,主要用于识别对象被GC的时机,通常用于做一些清理工作。

Garbage Collector(垃圾收集器)

HotSpot 1.6版使用的垃圾收集器如下图(图来源于《深入理解Java虚拟机:JVM高级特效与最佳实现》,图中两个收集器之间有连线,说明它们可以配合使用):

Java垃圾回收机制

1)Serial新生代串行收集器

串行收集器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收。第二,它是独占式的垃圾回收。当JVM在Client模式下运行时,它是默认的垃圾收集器。

2)Serial Old老年代串行收集器

老年代串行收集器使用的是标记-压缩算法。和新生代串行收集器一样,它也是一个串行的、独占式的垃圾回收器。由于老年代垃圾回收通常会使用比新生代垃圾回收更长的时间,因此,在堆空间较大的应用程序中,一旦老年代串行收集器启动,应用程序很可能会因此停顿几秒甚至更长时间。虽然如此,老年代串行回收器可以和多种新生代回收器配合使用,同时它也可以作为 CMS 回收器的备用回收器。若要启用老年代串行回收器,可以尝试使用以下参数:-XX:+UseSerialGC: 新生代、老年代都使用串行回收器。

3)并行收集器

ParNew并行收集器是工作在新生代的垃圾收集器,它只简单地将串行回收器多线程化。它的回收策略、算法以及参数和串行回收器一样。并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。开启并行回收器可以使用参数-XX:+UseParNewGC,该参数设置新生代使用并行收集器,老年代使用串行收集器。-XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。

4)新生代并行回收 (Parallel Scavenge) 收集器

新生代并行回收收集器也是使用复制算法的收集器。从表面上看,它和并行收集器一样都是多线程、独占式的收集器。但是,并行回收收集器有一个重要的特点:它非常关注系统的吞吐量,追求高效利用CPU,吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。
新生代并行回收收集器可以使用以下参数启用:
-XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。
-XX:+UseParallelOldGC:新生代和老年代都是用并行回收收集器。

5)老年代并行回收收集器

老年代的并行回收收集器也是一种多线程并发的收集器。和新生代并行回收收集器一样,它也是一种关注吞吐量的收集器。老年代并行回收收集器使用标记-压缩算法,JDK1.6 之后开始启用。使用-XX:+UseParallelOldGC 可以在新生代和老生代都使用并行回收收集器,这是一对非常关注吞吐量的垃圾收集器组合,在对吞吐量敏感的系统中,可以考虑使用。参数-XX:ParallelGCThreads 也可以用于设置垃圾回收时的线程数量。

6)CMS 收集器

与并行回收收集器不同,CMS 收集器主要关注于系统停顿时间。CMS是Concurrent Mark Sweep的缩写,意为并发标记清除,从名称上可以得知,它使用的是标记-清除算法,同时它又是一个使用多线程并发回收的垃圾收集器。CMS工作时,主要步骤有:初始标记、并发标记、重新标记、并发清除和并发重置。其中初始标记和重新标记是独占系统资源的,而并发标记、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上来说,CMS 收集不是独占式的,它可以在应用程序运行过程中进行垃圾回收。

7)G1 收集器 (Garbage First)

G1 收集器的目标是作为一款服务器的垃圾收集器,因此,它在吞吐量和停顿控制上,预期要优于 CMS 收集器。与CMS收集器相比,G1收集器是基于标记-压缩算法的。因此,它不会产生空间碎片,也没有必要在收集完成后,进行一次独占式的碎片整理工作。G1 收集器还可以进行非常精确的停顿控制。它可以让开发人员指定当停顿时长为 M时,垃圾回收时间不超过 N。使用参数-XX:+UnlockExperimentalVMOptions –XX:+UseG1GC 来启用 G1 回收器,设置 G1 回收器的目标停顿时间:-XX:MaxGCPauseMills=20,-XX:GCPauseIntervalMills=200。

 

 

参考:

http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

http://coolshell.cn/articles/11541.html

http://www.ibm.com/developerworks/cn/java/j-lo-JVMGarbageCollection/index.html

 

你可能感兴趣的:(垃圾回收机制)