在Java运行时的几个数据区域中,程序计数器,虚拟机栈,本地方法栈3个区域随着线程而生,随线程而灭,因此这几个区域的内存分配和回收具有确定性,不需要过多考虑垃圾回收问题,因为方法结束或者线程结束时,内存就回收了。但是方法区和堆区不一样,一个接口或者实现类所需要的内存可能不一样,一个方法的多个分支需要的内存也可能不一样,只有程序运行时才能知道创建哪些对象,这部分内存的分配和回收是动态的。

  在进行垃圾回收时候,首先需要判断哪些对象需要回收,这就涉及到回收算法的问题。

一、垃圾回收算法

1.标记-清除算法

  标记-清除算法是一种最基础的垃圾收集算法,分为“标记”和“清除”两步。“标记”阶段标记所有需要进行垃圾回收的对象,标记完成后统一回收被标记的对象。这种算法的不足点在于:

  (1)效率问题,标记和清除两个过程效率都不高;

  (2)空间问题,标记清除后会产生大量不连续碎片,后续如果需要为较大对象分配空间,则又需触发垃圾回收。

2.复制算法

  为了解决标记-清除算法的效率问题,出现了复制算法。这种算法把内存按照容量划分为大小相同的两块,每次只是用其中一块,当这块内存用完了,就把还存活的对象复制到另外一块中,并将这块的内存清理掉,然后使用另外一块,当另外一块内存用完了,再把存活的对象复制到这块中,并清理另外一块内存,依次类推。

  复制算法主要用于新生代的回收,在HotSpot虚拟机中,新生代内存划分为一块较大的Eden空间,和两块较小的Survivor空间,每次使用Eden空间和其中一块Survivor空间。当进行垃圾回收时,会把Eden空间和Survivor空间中存活的对象一次性复制到另外一块Survivor空间上,最后清理掉Eden空间和刚才使用过的Survivor空间。HotSpot虚拟机中,默认情况下Eden空间和Survivor空间的大小比例是8:1,即Eden空间占整个新生代的80%,每次新生代中使用的空间为80%+10%=90%,闲置空间10%。

3.标记-整理算法

  复制算法适用于那种对象存活率较低的场景,在对象存活率较高时,使用复制收集算法意味着需要进行大量复制,会使效率降低,同时复制大量存活对象到另外一块内存,意味着需要有足够大的内存来保存这些对象,这势必会降低内存使用率。根据老年代的特点,有人提出标记-整理算法,和标记-清除算法不同的是,标记整理算法将存活的对象向一端移动,然后直接清理掉端边界之外的内存。

4.分代收集算法

  目前商业虚拟机中都使用分代收集算法。一般将Java堆分为新生代和老年代,新生代进行垃圾收集发现有大量对象死去,只有少量对象存活,那么就使用复制算法。老年代中对象存活率较高,使用标记-清除算法或者标记-整理算法。

二、垃圾收集器

  垃圾收集算法提供了内存回收的方法论,垃圾收集器是内存回收的方法论。每个厂商对垃圾收集器的实现不一样,这里主要讨论Jdk1.7 Update 14之后的HotSpot虚拟机。这个虚拟机中包含的垃圾收集器有如下7种:

    以上收集器之间如果有连线,则表明可以搭配使用,虚拟机所处区域,表示他是新生代收集器还是老年代收集器。

1.Serial收集器

  Serial收集器是一种最基本的单线程收集器,这种收集器工作时,必须停止其他所有工作线程,优点在于简单高效,但体验很不友好,目前主要应用场合是:虚拟机运行在Client模式下的默认新生代收集。器。

2.ParNew收集器

  parNew收集器是Serial收集器的多线程版本,常用参数设置:

   -XX:+UseConcMarkSweepGC :设置ParNew为默认的新生代收集器;

   -XX:+UseParNewGC :指定使用ParNew为年轻代收集器,强制指定;

   -XX:ParallelGCThreads=n :设置收集器的线程数为n。

3.Parallel Scavenge收集器

  Parallel Scavenge收集器是一个使用复制算法的新生代收集器,这种收集器的主要目标是达到一个可控制的吞吐量(Throughput,CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。由于与吞吐量关系密切,故而Parallel Scavenge收集器也称为“吞吐量优先”收集器。常用参数设置:

   -XX:MaxGCPauseMillis=n :设置年轻代垃圾收集的最长时间;

   -XX:GCTimeRatio=n :设置垃圾收集总可用时长的比例,和吞吐量直接相关;

   -XX:+UseAdaptiveSizePolicy 自适应大小开关,配置该选项之后,每次GC后会重新计算 Eden、From 和 To 区的大小,计算依据是 GC 过程中统计的 GC 时间、吞吐量、内存占用量,因此设置此参数之后就不需要再设置 -XX:SurvivorRatio 、 -XX:PretenureSizeThreshold 等参数了。

4.Serial Old收集器

  Serial收集器的老年版本,也是一个单线程收集器,使用的是“标记-整理”算法,这种收集器的主要意义也是给Client模式下的虚拟机使用。

5.Parallel Old收集器

  Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge收集器和Parallel Old收集器的组合。

6.CMS收集器

  CMS收集器是一种以获取最短回收停顿时间为目标的收集器。它基于“标记-清除”算法实现,运作过程相对于其他几种收集器更复杂一些。分为以下四个过程:

  (1)初始标记(CMS initial mark):标记一下CG Roots能关联到的对象;

  (2)并发标记(CMS concurrent mark):进行CG Roots Tracing的过程;

  (3)重新标记(CMS remark):修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。

  (4)并发清理(CMS concurrent sweep)

  CMS 收集器的优点在于并发收集,低停顿。其缺点在于以下三点:

  (1)CMS收集器对CPU很敏感,CMS默认回收线程是(CPU数量+3)/4,当CPU在4个以上时,并发收集时垃圾收集线程不少于25%的CPU资源,并随着CPU数量增加而下降。但是当CPU不足4个时,CMS对用户程序的影响就会变得很大。

  (2)CMS收集器无法处理浮动垃圾。由于CMS收集器并发清理阶段用户线程还在运行着,伴随着程序运行就会有垃圾产生,这部分垃圾在标记过后,CMS收集器无法在当次收集中清理这些垃圾。

  (3)由于CMS收集器是一种基于“标记-清除”算法的收集器,这种算法实现的收集器在收集结束后会有大量不连续碎片产生。碎片过多时会给大对象分配带来很大麻烦,往往老年代还有很大空间剩余,但是无法找到连续空间分配当前对象,因而不得不提前触发Full GC。

7.G1收集器

  G1收集器是一款面向服务端应用的垃圾收集器,与其他收集器相比,G1收集器具有如下优点:

  (1)并发与并行:G1能充分利用多CPU,多核硬件优势,使用多个CPU来减少停顿时间;

  (2)分代收集:G1不需要其他收集器配合就能独立管理整个堆的垃圾收集,且它能采用不同方式去处理新建对象和已经存活了一段时间,熬过多次GC的旧对象以获得更好的收集效果。

  (3)空间整合:使用G1收集器不会产生内存碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时候不会因为无法找到连续内存空间而提前触发下一次GC.

  (4)可预测的停顿:G1除了追求低停顿,还能建立可预测的停顿时间模型,能让使用着指定在长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不超过N毫秒。

三、垃圾收集参数总结

参数 描述
UseSerialGC

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

UseParNewGC 打开此开关后,使用ParNew + Serial Old 的收集器组合进行内存回收
UseConcMarkSweepGC 打开此开关后,使用ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将作为CMS收集器出现Concurrent Mode Failure失败后的后备收集器使用
UseParallelGC 虚拟机运行在Server 模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器组合进行内存回收
UseParallelOldGC 打开此开关后,使用Parallel Scavenge + Parallel Old 的收集器组合进行内存回收
SurvivorRatio 新生代中Eden 区域与Survivor 区域的容量比值,默认为8,代表Eden :Survivor=8∶1
PretenureSizeThreshold 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配
MaxTenuringThreshold 晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC 之后,年龄就加1,当超过这个参数值时就进入老年代
UseAdaptiveSizePolicy 动态调整Java 堆中各个区域的大小以及进入老年代的年龄
HandlePromotionFailure 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden 和Survivor 区的所有对象都存活的极端情况
ParallelGCThreads 设置并行GC 时进行内存回收的线程数
GCTimeRatio GC 时间占总时间的比率,默认值为99,即允许1% 的GC 时间。仅在使用Parallel Scavenge 收集器时生效
MaxGCPauseMillis 设置GC 的最大停顿时间。仅在使用Parallel Scavenge 收集器时生效
CMSInitiatingOccupancyFraction 设置CMS 收集器在老年代空间被使用多少后触发垃圾收集。默认值为68%,仅在使用CMS 收集器时生效
UseCMSCompactAtFullCollection 设置CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在使用CMS 收集器时生效
CMSFullGCsBeforeCompaction 设置CMS 收集器在进行若干次垃圾收集后再启动一次内存碎片整理,仅在使用CMS 收集器时生效

参考资料:《深入理解Java虚拟机 JVM高级特性与最佳实践 第2版》

Java虚拟机四:垃圾回收算法与垃圾收集器的更多相关文章

  1. 3-JVM垃圾回收算法和垃圾收集器

    垃圾回收算法和垃圾收集器 1.什么是垃圾回收 对于内存当中无用的对象进行回收,如何去判断一个对象是不是无用的对象. 引用计数法: 每个对象中都会存储一个引用计数,每增加一个引用就+1,消失一个引用就- ...

  2. java垃圾回收算法和垃圾收集器

    垃圾收集算法.垃圾回收算法.java垃圾收集器 目录1. 垃圾收集算法1)引用计数法2)根搜索法2. 垃圾回收算法1)复制算法2)标记-清除算法3)标记-整理算法4)分代收集算法3. java垃圾收集 ...

  3. 深入理解java虚拟机【垃圾回收算法】

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  4. Java虚拟机之垃圾回收算法思想总结

    1.引用计数法 这是个比较古老而经典的垃圾回收算法,其核心就是在对象被其他所引用的时候计数器加1,而当引用失去时减1.这个方法有非常严重的问题:无法此话有理循环引用的情况,还有就是每次进行加减操作比较 ...

  5. JVM垃圾回收算法 及 垃圾收集器

    摘自<深入理解Java虚拟机> 一.什么是: GC算法是 方法论,那么垃圾收集器就是具体的 实现. 二.四种 垃圾回收算法 1.标记-清除算法:最基础的收集算法:不足有两点:1标记和清除两 ...

  6. 6.GC垃圾回收算法和垃圾收集器的关系

    JAVAGC垃圾回收机制和常见垃圾回收算法 推荐博客:JVM垃圾回收机制和常见垃圾回收算法 JVM的内存结构.垃圾回收算法

  7. 《深入理解java虚拟机》笔记(5)垃圾回收算法及垃圾收集器

    一.标记-清除算法 算法:分为标记和清除两个阶段,首先标记出所有需要回收的对象,再对标记对象进行回收. 不足之处:效率不高,会产生大量不连续内存碎片,导致下次分配较大内存时,若内存不足不得不触发垃圾回 ...

  8. java虚拟机之垃圾回收算法

    标记-清除算法: 这是最基础的,就是之前所讲的两次标记,首先标记出所有 需要回收的对象,然后进行统一清除, 这有两缺点:一是效率低,标记和清除(开启低优先级进行回收)都是低效率的.第二是空间问题,标记 ...

  9. 每日一问:讲讲 Java 虚拟机的垃圾回收

    昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...

随机推荐

  1. Codeforces 19.E Fairy

    E. Fairy time limit per test 1.5 seconds memory limit per test 256 megabytes input standard input ou ...

  2. poj3648 Wedding

    Wedding Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10975   Accepted: 3355   Specia ...

  3. 如何将下载的web工程导入到eclipse中使用

    如果你是喜欢编程的,在你的开发工具中一定有许多项目,就像小编一样(PS:小编只想默默地装一X):   我们选中其中的一个项目,然后[Ctrl + C]复制,再[Ctrl + V]粘贴到桌面:   那么 ...

  4. ubuntu 安装部分设置U盘启动系统安装盘操作

    https://jingyan.baidu.com/article/6079ad0ec4925728ff86db02.html 第三种方法适合新机型,在启动时按F10或F11,可直接选择启动模式 -- ...

  5. CF767 B. The Queue 贪心+细节

    LINK 题意:一个业务开始时间为s,结束时间为f,一个人办护照的时间需要m分(如果在x时开始服务,且x+m==f那么还是合法的),你可以选择任意时间到达,但如果你和其他人同时到达,你要排在他的后面. ...

  6. JAVA多线程提高三:线程范围内共享变量&ThreadLocal

    今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用. 一.概念 可以将每个线程用到的数据与对应的线程号存放到一 ...

  7. CSS预处理器们

    CSS预处理器有很多,最早的是2006年的Less,到后来2010年的SASS,还有现在也很出名的Stylus.不过要使用它们都要使用一些工具,比如Less的话要使用Grunt或者Gulp或者Node ...

  8. jQuery中一些不常用的方法属性【转载】

    index(subject) 搜索与参数表示的对象匹配的元素,并返回相应元素的索引值.如果找到了匹配的元素,从0开始返回:如果没有找到匹配的元素,返回-1. data() data(elem):为页面 ...

  9. 更改gradle的java的class文件输出目录的结构

    group 'com.thinkvenus.common'version '1.0-SNAPSHOT' apply plugin: 'java' sourceCompatibility = 1.8 r ...

  10. 始终要重载toString

    本文涉及到的概念 1.重载toString方法的意义 2.两个注意事项   1.重载toString方法的意义 重载toString,返回关于当前实例的描述信息.这在调试错误,打印实例信息时,可以带来 ...