JVM学习垃圾收集器

1.作用

  用来替代手工内存管理,减轻程序员的负担,减少出错的概率。
垃圾收集器要处理的基本问题是:

  • 那些对象需要回收?
  • 何时回收这些对象?
  • 如何回收这些对象?

2.垃圾回收算法与思想

1.引用计数法(Reference Counting)

  对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减一。只要对象A的引用计数器的值为0,则对象A就不可能再被使用。
  引用计数器无法处理循环引用的情况,因此,Java的垃圾回收器中,没有使用这种算法。

2. 标记-清除算法(Mark-Sweep)

说明:标记-清除算法是现代垃圾回收算法的思想基础。将垃圾回收分为两个阶段:标记阶段和清除阶段。在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后在清除阶段,清除所有未被标记的对象。
缺点:标记-清除算法可能产生的最大问题就是空间碎片。垃圾回收后的空间是不连续的。在对象的堆空间分配过程中,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续的空间,这也是该算法的最大缺点。

3.复制算法(Copying)

  • 说明:与标记-清除算法相比,复制算法是一种相对高效的回收方法。将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
  • 优点:如果系统中的垃圾对象很对,复制算法需要复制的存活对象数量并不会太大。因此,在真正需要垃圾回收的时刻,复制算法的效率是很高的。又由于对象是在垃圾回收过程中统一被复制到新的内存空间中,因此,可确保回收后的内存空间是没有碎片的。
  • 缺点:复制算法的代价缺点是将系统内存折半,所以单纯的复制算法也很难让人接受。
  • 注意:复制算法比较适用于新生代,因为在新生代,垃圾对象通常会多余存活对象,复制算法的效果会比较好。

4.标记-压缩算法(Mark-Compact)

  • 说明:标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。
  • 优点:既避免了碎片的产生,又不需要两块相同的内存空间,因此其性价比较高。
  • 缺点:
  • 注意:

5.增量算法(Incremental Collecting)

  • 说明:对大部分垃圾回收算法而言,在垃圾回收过程中,应用软件将处于一种Stop the World的状态。在Stop the World状态下,应用程序所有的线程都会挂起,暂停一切正常的工作,等待垃圾回收的完成。如果垃圾回收时间很长,将严重影响用户体验或者系统的稳定性。
    增量算法的基本思想是,如果一次性将所有的垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程和应用程序线程交替执行。每次,垃圾手机现场只收集一小片区域的内存空阿金,接着切换到应用程序线程。以此反复,直到垃圾收集完成。
  • 优点:由于在垃圾回收的过程中,间断性地还执行了应用程序代码,所以能减少系统的停顿时间。
  • 缺点:因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。

6.分代(Generational Collecting)

  • 说明:根据垃圾回收对象的特性,使用合适的算法回收,才是明智的选择。将内存区间根据对象的特点分成几块,根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。
    如:

3.垃圾收集器的类型

4.评价GC策略的指标

  • 吞吐量:指在应用程序的什么周期内,应用程序所花费的时间和系统的总运行时间的比值。系统总运行时间=应用程序耗时+GC耗时。
  • 垃圾回收器负载:和吞吐量相反,垃圾回收期负载指垃圾回收器耗时与系统运行总时间的比值。
  • 停顿时间:指垃圾回收器正在运行时,应用程序的暂停时间。对于独占回收器而言,停顿时间可能会比较长。使用并发的回收器时,由于垃圾回收和应用程序交替运行,程序的停顿时间会变短,但是由于其效率很可能不如独占垃圾回收器,股系统的吞吐量可能会较低。
  • 垃圾回收频率:指垃圾回收器多长时间会运行一次。一般来说,对于固定的应用而言,垃圾回收器的评率应该是越低越好。通常增大堆空间可以有效降低垃圾回收发生的评率,但是可能会增加回收产生的停顿时间。
  • 反应时间:指当一个对象称为垃圾后,多长商检内,它所占据的内存空间会被释放。
  • 堆分配:不同的垃圾回收器对堆内存的分配方式可能是不同的。一个良好的垃圾收集器应该有一个合理的堆内存区间划分。

5.新生代串行收集器

  • 说明:串行收集器是所有垃圾收集器中最古老的一种,也是JDK种最基本的垃圾收集器之一。串行收集器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收;第二,它是独占式的垃圾回收。
  • 使用场景:在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合,它的性能表现可以超过并行回收器和并发回收器。
  • 使用方式:-XX:+UseSerialGC

6.老年代串行收集器

  • 说明:老年代串行收集器使用的是标记-压缩算法。和新生代串行收集器一样,它也是一个串行的、独占式的垃圾回收器。由于老年代垃圾回收通常会使用比新生代垃圾回收更长的时间。因此,在堆空间较大的应用程序中,一旦老年代串行收集器启动,应用程序很可能会因此停顿几秒甚至更长时间。
  • 使用方式: -XX:+UseSerialGC:新生代、老年代都使用串行回收器。
    -XX:+UseParNewGC:新生代使用并行收集器,老年代使用串行收集器。
    -XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。

7.并行收集器

  • 说明:并行收集器是工作在新生代的垃圾收集器,它只是简单地将串行回收器多线程化。并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于并行回收器使用多线程进行垃圾回收,因此,在并发能力比较强的CPU上,它产生的停顿时间要短于串行回收器,而在单CPU或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好。由于多线程的压力,它的实际表现很可能比串行回收器差。
  • 使用方式:-XX:+UseParNewGC:新生代使用并行收集器,老年代使用串行收集器。
    -XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS。
    -XX:ParallelGCThreads:一般最好与CPU数量相当,避免过多的线程数,影响垃圾收集性能。(默认情况下,CPU数量小于8个时,该值等于CPU的数量;当CPU数量大于8个时,该值等于3+[(5*CPU_Count)/8])。

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

  • 说明:新生代并行回收收集器也是使用复制算法的收集器。它和并行收集器一样,都是多线程、独占式的收集器。
  • 特点:它非常关注系统的吞吐量。
  • 使用方式:-XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。
    -XX:+UseParallelOldGC:新生代和老年代都使用并行回收收集器。
    -XX:MaxGCPauseMillis:设置最大垃圾收集停顿时间,它的值是一个大于0的整数。收集器在工作是,会调整Java堆大小或者其他一些参数,尽可能地把停顿时间控制在MaxGCPauseMillis以内。如果希望减少停顿时间,而把这个值设置得很小,为了达到预期的停顿时间,JVM可能会使用一个较小的堆,而这将导致垃圾回收变得很频繁,从而增加了垃圾回收总时间,降低了吞吐量。
    -XX:GCTimeRatio:设置吞吐量大小,它的值是一个0~100之间的整数。假设GCTimeRatio的值为n,那么系统将花费不超过1/(1+n)的时间用于垃圾收集。比如GCTimeRatio等于19(默认值),则系统用于垃圾收集的时间不超过1/(1+19)=5%。默认情况下,它的取值是99,即不超过1%的时间用于垃圾回收。
  • 不同点:并行回收收集器与并行收集器比较,它支持一种自适应的GC调节策略。
    -XX:+UseAdaptiveSizePolicy 可以打开自适应GC策略。在这种模式下,新生代的大小、eden和survivor的比例、晋升老年代的对象年龄等参数会被自动调节,以达到在堆大小、吞吐量和停顿时间之间的平衡点。在手工调优比较困难的场合,可以仅指定虚拟机的最大堆、目标的吞吐量、和停顿时间,让虚拟机自己完成调优工作。

9.老年代并行回收收集器

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

10.CMS收集器

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

    根据标记-清除算法,初始标记、并发标记和重新标记都是为了标记处需要回收的对象。并发清理,则是在标记完成后,正式回收垃圾对象;并发重置是指在垃圾回收完成后,重新初始化CMS数据结构和数据,为下一次垃圾回收做好准备。并发标记、并发清理和并发重置都是可以和应用程序线程一起执行的。
    CMS收集器在其主要的工作阶段虽然没有暴力地彻底暂停应用程序线程,但是由于它和应用程序线程并发执行,相互抢占CPU,故在CMS执行期内对应用程序吞吐量将造成一定影响。CMS默认启动的线程数是(ParallelGCThreads+3)/4,ParallelGCThreads是新生代并行收集器的线程数,也可以通过 -XX:ParallelCMSThreads参数手工设置CMS的线程数量。当CPU资源比较紧张时,受到CMS收集器线程的影响,影响系统的性能在垃圾回收阶段可能会非常糟糕。
    缺点:由于CMS收集器不是独占式的回收器,在CMS回收过程中,应用程序仍然在不停地工作。在工作过程中,又会不断地产生垃圾。这些新生产的垃圾在当前CMS回收过程中是无法清除的。同时,因为应用程序没有中断,故在CMS回收过程中,还应该确保有足够的内存可用。因此,CMS收集器不会等待堆内存饱和时才进行垃圾回收,而是当堆内存使用率达到某一阈值时,便开始进行回收。这个回收阈值可以使用 -XX:CMSInitiatingOccupancyFraction来指定。默认是68.即当老年代的空间使用率达到68%时,会执行一次CMS回收。如果应用程序的内存使用率增长很快,在CMS的执行过程中,已经出现了内存不足的情况,此时,CMS回收就会失败,JVM将启动老年代串行收集器进行垃圾回收。如果这样,应用程序将完全中断,直到垃圾收完成,这时,应用程序的停顿时间可能很长。
  • 使用方式:-XX:CMSInitiatingOccupancyFraction 如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低CMS的除法频率,减少老年代回收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。
    -XX:+UseCMSCompactAtFullCollection 开关可以使CMS在垃圾回收完成后,进行一次内存碎片整理。内存碎片整理不是并发进行的。
    -XX:CMSFullGCsBeforeCompaction 指定多少次CMS回收后,进行一次内存压缩。

11.G1收集器(Garbage First)

  • 说明:G1收集器在JDK1.6_Update14中提供了早期预览版,并伴随JDK7发布。G1收集器的目标是作为一款服务端的垃圾收集器,因此,它在吞吐量和停顿控制上,预期要优于CMS收集器。该收集器是基于标记-压缩算法。
  • 优点:1.不会产生空间碎片。2.不需要进行独占式的碎片整理。3.可以进行非常精准的停顿控制。在指定长度为M的时间段中,垃圾回收时间不超过N。
  • 使用方式:-XX:+UnlockExperimentalVMOptions
    -XX:+UseG1GC
    -XX:MaxGCPauseMillis=50
    -XX:GCPauseIntervalMillis=200 意为在200ms内,停顿时间不超过50ms。这两个参数是GL回收器的目标,但并不保证能执行它们。

12.总结

1.与串行回收器相关的参数

-XX:UseSerialGC:在新生代和老年代使用串行收集器。
-XX:SurvivorRatio:设置eden区大小和survivor区大小的比例。
-XX:PretenureSizeThreshold:设置大对象直接进入老年代的阈值。当对象的大小超过这个值时,直接在老年代分配。
-XX:MaxTenuringThreshold:设置对象进入老年代的年龄的最大值。每一次Minor GC后,对象的年龄就加一,任何大于这个年龄的对象,一定会进入老年代。

2.与并行GC相关的参数

-XX:+UseParNewGC 在新生代使用并行收集器
-XX:+UseParallelOldGC 老年代使用并行回收收集器
-XX:ParallelGCThreads 设置用于垃圾回收的线程数。通常情况下可以和CPU数量相等。但在CPU数量比较多的情况下,设置相对较小的数值也是合理的。
-XX:MaxGCPauseMillis 设置最大垃圾收集停顿时间。它的值是一个大于0的整数。收集器在工作时,会调整Java堆大小或者其他一些参数,尽可能地把停顿时间控制在MaxGCPauseMillis以内。
-XX:GCTimeRatio 设置吞吐量大小,它的值是一个0~100之间的整数。假设GCTimeRatio的值为n,那么系统将花费不超过1/(1+n)的时间用于垃圾回收。
-XX:+UseAdaptiveSizePolicy 打开自适应GC策略。新生代的大小、eden和survivor的比例、晋升老年代的对象年龄等参数会被自动调整,已达到在堆大小、吞吐量和停顿时间之间的平衡点。

3.与CMS回收器相关的参数

-XX:+UseConcMarkSweepGC 新生代使用并行收集器,老年代使用CMS+串行收集器。
-XX:ParallelCMSThreads 设置CMS的线程数量。
-XX:CMSInitiatingOccupancyFraction 设置CMS收集器在老年代空间被使用多少后触发,默认68%。
-XX:+UseCMSCompactAtFullCollection 设置CMS收集器在完成垃圾收集后是否进行一次碎片整理。
-XX:CMSFullGCsBeforeCompaction 设定多少次CMS垃圾回收后进行一次内存压缩。
-XX:+CMSClassUnloadingEnabled 允许对类元数据进行回收。
-XX:+CMSParallelRemarkEnabled 启用并行重标记。
-XX:CMSInitiatingPermOccupancyFraction 当永久区占用率达到这一百分比时,启动CMS回收(前提是 -XX:+CMSClassUnloadingEnabled激活了)。
-XX:UseCMSInitiatingOccupancyOnly 表示只在到达阈值的时候,才进行CMS回收。
-XX:CMSIncrementalMode 使用增量模式,比较适合单CPU。

4.与G1回收器相关的参数

-XX:+UseG1GC 使用G1回收器。
-XX:+UnlockExperimentalVMOptions 允许使用实验性参数。
-XX:MaxGCPauseMillis 设置最大垃圾收集停顿时间。
-XX:GCPauseIntervalMillis 设置停顿时间间隔。

5.其他参数

-XX:+DisableExplicitGC 禁用显示GC。
-XX:MinHeapFreeRatio 设置堆空间最小空闲比例,默认是40,当堆空间的空闲内存小于这个数值时,JVM便会扩展堆空间。
-XX:MaxHeapFreeRatio 设置堆空间的最大空闲比例,默认是70。当堆空间的空闲内存大于这个数值时,便会压缩堆空间,得到一个较小的堆。(当-Xms和-Xmx相等时,-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio是无效的
-XX:LargePageSizeInBytes 设置大页的大小。
-XX:TargetSurvivorRatio 设置survivor区的可使用率。这里设置为90%,则允许90%的survivor空间被使用。默认值是50%。故该设置提高了survivor区的使用率。当存放的对象超过这个百分比,则对象会向老年代压缩。因此,这个选项更有助于将对象留在新生代。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值