`

《垃圾收集器与内存分配策略》笔记:垃圾收集算法和垃圾收集器

阅读更多

         周志明先生所著的《深入理解Java虚拟机:JVM高级特性与最佳实践》(购买地址:亚马逊链接),对我学习Java、理解Java之道有非常大的帮助。至今已读过两遍,为了能够融会贯通,加深记忆(人老了记忆力差),便在Blog上记录一些认为该记的东西。

根搜索算法

         堆中几乎存放着Java世界中所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象有哪些还“存活”这,哪些已经“死去”(即不可能再被任何途径使用的对象)

       主流的商用程序语言,都是使用根搜索算法判定对象是否存活着。这个算法的基本思想就是通过一系列的名为“GC Roots”的对象做起起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

         Java里,可作为GC Roots的对象包括下面几种:

         1、虚拟机栈中的引用的对象。

         2、方法区中的类静态属性引用的对象。

         3、方法区中的常量引用的对象。

         4、本地方法栈中JNI的引用的对象。

 

JDK1.2之前,Java中的引用定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。

JDK1.2之后,Java将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。

强引用就是指在程序代码中普片存在的,类似“Object obj=new Object()”这类引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

软引用用来描述一些还有用,但并非必需的对象。JVM在将要发生内存溢出前,会把这些对象回收。

弱引用也是用来描述非必需对象的。当垃圾收集器工作时,无论当前内存是否足够,弱引用对象都会被回收。

虚引用是最弱的一种引用关系。

 

垃圾收集算法

标记—清除算法  首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。该算法主要有两个缺点:一个是效率问题;另外一个是空间问题,会产生大量不连续的内存碎片。

 

复制算法        将可用内存分为两块,每次只是用其中的一块。当这一块的内存用完了,就把其中存活的对象复制到另外一块上面,然后再把已经使用过的内存空间一次清理掉。

         现在商用的虚拟机都采用这种收集算法来回收新生代。在Hotspot中,jvm将内存分为一块较大的Eden空间和两块较小的Survivor空间(from spaceto space),每次使用Eden和其中的一块survivor(from space)。当回收时,将还存活的对象一次性地拷贝到另外一块survivor空间(to space)上,最后清理掉Eden和刚才使用的Survivor空间。Hotspot默认EdenSurvivor的大小比例是8:1 。如果to space没有足够的空间存放上一次新生代收集后的存活对象,这些对象将直接进入老年代。

 

         标记—整理算法  标记过程与“标记—清除算法”一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

 

         分代收集算法       根据对象的存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法。而老年代中因为对象存活率搞、没有额外空间进行空间分配担保,就必须使用“标记—整理”或“标记—清除”算法。当前商业虚拟机一般都采用分代收集。

 

 

垃圾收集器

Hotspot虚拟机1.6Update22的垃圾收集器如下图所示:



 

 

Serial收集器           这是一个单线程收集器。它进行垃圾收集时,必须暂停其他所有的工作线程(Stop The World),直到收集结束。它是虚拟机运行在Client模式下的默认新生代收集器。

 

ParNew收集器      ParNew其实就是Serial收集器的多线程版本,使用了多线程来进行垃圾收集。它是许多运行在Server模式下的虚拟机首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。

ParNew收集器在单CPU环境中不会比Serial收集器效率更高,甚至由于存在线程交互的开销,该收集器在通过超线程技术实现的两个CPU的环境中,都不能百分之百地保证能超越serial收集器。它默认开启的收集线程数与CPU的数量相同。

 

Parallel Scavenge收集器      该收集器也是新生代收集器,也是使用复制算法,也是并行的多线程收集器。它的目标是达到一个可控制的吞吐量。吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,如虚拟机共运行了100分钟,其中垃圾收集花了1分钟,则吞吐量就是(100-1)/100=99(%)

 

 

老年代收集器:

Serial Old收集器            Serial Old收集器是Serial收集器的老年代版本,它也是个单线程收集器,使用“标记—整理”算法。是JVMclient模式下的默认老年代收集器。它在Server模式下有两大用途:一是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用;另一个就是作为CMS收集器的后备预案,在CMS收集失败时,JVM会使用Serial Old进行收集。

 

Parallel Old收集器      Parallel OldParallel Scavenge收集器的老年代版本,使用多线程和“标记—整理”算法。

 

CMS收集器             CMS收集器使用“标记—清除”算法。它是“并发”(可以和用户线程“同时”工作)收集的,低停顿的收集器。主要有4个步骤:

1、初始标记

2、并发标记

3、重新标记

4、并发清除

初始标记和重新标记这两步骤仍然需要“stop the world”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。并发标记阶段就是进行GC Roots Tracing过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。最后是耗时最长并发清除,收集器线程可以与用户线程一起工作。总体上来说,CMS收集器的内存回收过程与用户线程一起并发地执行。

 

CMS的缺点:

1、CMS在并发收集时,会与用户线程抢CPU资源……

2、CMS无法处理浮动垃圾。由于CMS在并发清理阶段,用户线程还在运行,自然会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,所以CMS无法在本次收集中处理它们,只能下一次GC时处理。由于CMS在收集阶段用户线程还需运行,JVM必须预留足够的内存空间给用户线程使用,所以,在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活。(可以通过参数来设置该百分比)

3、CMS基于“标记—清除”算法,则会产生空间碎片。可以通过参数开关来控制在FULL GC或若干次FULL GC后,进行碎片整理。为什么CMS不用“标记—整理”,可以参考这里:http://hllvm.group.iteye.com/group/topic/38223

 

G1收集器                G1(Garbage First)收集器是JDK1.7提供的一个新收集器,G1收集器基于标记-整理算法实现,也就是说不会产生内存碎片。还有一个特点之前的收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java(包括新生代,老年代)

 

垃圾收集器参数总结

 

参数名称

参数描述

UseSerialGC

虚拟机运行在client模式下的默认值,打开次开关后,使用Serial+Serial Old 的收集器组合进行内存回收。

UseParNewGC

打开次开关后,使用ParNew + Serial Old 的收集器组合进行内存回收。

UseConcMarkSweepGC

打开次开关后,使用ParNew + CMS +Serial Old的收集器组合进行内存回收。Serial Old收集器将作为CMS收集器出现Concurrent Mode Failure失败收的后备收集器。

UseParallelGC 

虚拟机运行在Server模式下的默认值,打开次开关后,使用ParallelScavenge + Serial Old的收集器组合进行内存回收。

UseParallelOldGC

打开次开关后,使用Parallel Scavenge + Parallel Old的收集器组合进行内存回收。

SurvivorRatio

新生代中Eden区与Survivor区的容量比值,默认是8,代表Eden:Survivor = 8:1

PretenureSizeThreshold

直接晋升到老年代对象的大小,设置这个参数后,大于这个参数的对象将直接在老年代分配。

MaxTenuringThreshold

晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC之后,年龄就加1,当超过这个参数值时就进入到老年代。

UseAdaptiveSizePolicy

动态调整Java堆中各个区的大小以及进入老年代的年龄。

HandlePromotionFailue

是否允许分配担保失败,即老年代空间不足以应对新生代的整个EdenSurvivor区的所有对象都存活的极端情况。

ParallelGCThreads

设置并行GC时进行内存回收的线程数

GCTimeRatio

GC时间占总时间的比率,默认值是99,仅在使用Parallel Scavenge收集器时生效。

MaxGCPauseMillis

设置GC的最大停顿时间,仅在使用Parallel Scavenge收集器时生效。

CMSInitiatingOccupancyFraction

设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认是68%,仅在使用CMS收集器时生效。

UseCMSCompactAtFullColletion

设置CMS收集器在完成垃圾收集后是否必要进行一次内存碎片整理。仅在使用CMS收集器时生效。

CMSFullGCsBeforeCompaction

设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS收集器时生效。

-Xint

禁用JIT编译。

-Xmn

设置新生代的大小

-Xms

设置初始堆的大小

-Xmx

设置最大推的大小

-XX:PermSize

设置永久代大初始大小

-XX:MaxPermSize

设置永久代的最大值

-Xss

设置虚拟机栈的大小。JDK1.5以前默认是256k,以后是1M

-XX:+DisableExplicitGC

禁用System.gc()

 

 

  • 大小: 43.2 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics