深入理解Java虚拟机之垃圾收集二

垃圾收集器

图示为JDK1.7 Update14之后的HotSpot虚拟机所使用的所有垃圾收集器

如果两个收集器存在连线,那么说明这两个收集器可以搭配使用。收集器所处的区域说明它是新生代收集器还是老年代收集器。

Serial收集器

主要看图↑ 上图演示了Serial/Serial Old收集器运行示意图。
Serial收集器是一个单线程收集器,该收集器在进行垃圾收集的同时,必须暂停(Stop the World!)其它工作线程(这就好比你妈妈在打扫房间的同时,肯定也会让你乖乖待在一边,如果她一边打扫,你一边扔纸屑,这房间怎么打扫呢?)。
相比其他新生代收集器,Serial更加简单高效(单线程和多线程相比较没有了线程交互的开销),所以到现在它依然是Client模式下的默认新生代收集器。

ParNew收集器

上图演示了ParNew收集器运行示意图。
ParNew收集器实际上就是Serial收集器的多线程版本,其他和Serial收集器没有本质的差距,如果非要找出不同点,emmm,它是运行在Server模式下的虚拟机中首选的新生代收集器。
parNew收集器在单CPU环境下不一定会比Serial有更出色的表现,但随着CPU核数的增加,它的优势就会显现出来。

Parallel Scavenge收集器

在介绍Parallel Scavenge收集器之前,笔者先为读者定义一个基本概念:吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾收集的时间),举个例子,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那么吞吐量就是99%。
Parallel Scavenge收集器相比较ParNew收集器最大的区别在于关注点不同,它所关注的是吞吐量而不是Stop the World的停顿时间。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高响应比则可以高效率的利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多的交互任务。

主要看图↑ 上图演示了Serial/Serial Old收集器运行示意图。
Serial Old收集器是Serial收集器的老年代版本,这个收集器主要使用在Client模式下,如果是用在Server模式下,分别可以和JDK1.5之前的版本中的Parallel Scavenge收集器搭配使用或者作为CMS收集器的后备预案。

Parallel Old收集器

上图演示了Parallel Scavenge/Parallel Old收集器运行示意图
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,它和Parallel Scavenge收集器的配合可以在整体应用上获得最大吞吐量的效果......
在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scanvenge和Parallel Old收集器。

CMS收集器

\

CMS( Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它主要使用在互联网网站和B/S系统的服务器上。
CMS收集器是基于标记-清除算法实现的,它运作主要包括四个过程

  1. 初始标记(CMS initial mark)
  2. 并发标记(CMS concurrent mark)
  3. 重新标记(CMS remark)
  4. 并发清除(CMS concurrent sweep)

初始标记和重新标记这两个步骤仍然需要”Stop the World"

初始标记

初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。

并发标记

并发标记阶段就是进行GC Roots Tracing的过程。

重新标记

重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录

CMS收集器的缺点:

  1. CMS收集器对CPU资源非常敏感
  2. CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure"
  3. CMS采用标记-清除算法进行垃圾回收,算法自身的缺点会导致CMS产生大量的空间碎片产生

G1收集器

上图为G1(Garbage First)收集器的运行示意图
G1是一款面向服务端应用的垃圾收集器。
G1收集器相比其他的垃圾收集器主要有以下优点:

  1. 并行与并发,G1能充分利用多CPU,多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop The World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
  2. 分代收集,与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获得更好的收集效率。
  3. 空间整合,与CMS的标记-清除算法不同,G1从整体来看是基于标记整理算法实现的收集器,从局部(两个Region之间)上来看是基于复制算法实现的。但无论如何,这两种算法都意味着G1运行期间不会产生内存空间碎片,收集后能提供规整的可用内存,这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC
  4. 可预测的停顿,这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时JAVA(RTSJ)的垃圾收集器的特征了。
    补充:使用G1收集器时,JAVA堆的内存布局与其它收集器有很大的区别,它将整个JAVA堆划分为大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。

内存分配与回收策略

对象的内存分配,往大方向上讲,就是在堆上分配(但也可能经过JIT编译后被拆散为标量类型并间接地在栈上分配),对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。

  1. 对象优先在Eden分配,大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。
  2. 大对象直接进入老年代,大对象(需要大量连续内存空间的JAVA对象,最典型的大对象就是那种很长的字符串以及数组)对虚拟机来说就是一个坏消息,尤其是朝生夕灭的大对象,在写程序的时候要避免使用大对象。
  3. 长期存活的对象将进入老年代

虚拟机采用分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,当被移动到Survivo空间中,并且对象年龄设为1,对象在Survivor区中每”熬过“一次Minor GC,年龄就增减1岁,当它的年龄增加到一定程度(默认15岁),将会被晋升到老年代中。对象晋升老年代的阈值可以通过虚拟机参数来设置。

  1. 动态对象年龄判定。
  2. 空间分配担保,内存空间的分配担保就好比我们去银行贷款,如果我们信誉良好,在98%的情况下都能按时偿还,于是银行可能会默认我们下一次也能按时按量的偿还贷款,只需要有一个人能保证我不能还款时,可以从他的账户里面扣钱,那银行就认为我没有风险了。

补充

  1. 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
  2. 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但并不一定是并行的,可能会交替执行),用户程序在继续执行,而垃圾收集程序运行在另一个CPU上。
  3. 新生代(Minor GC):指发生在新生代的垃圾收集动作。Minor GC非常频繁,一般回收速度也比较快。
  4. 老年代(Major GC/Full GC):指发生在老年代的GC,出现Major GC,经常会伴随至少一次的Minor GC。Major GC的速度一般回比Minor GC慢10倍以上。

本文版权归作者0xTsmon和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

原文地址:https://www.cnblogs.com/LeeTsmon/p/9144489.html

时间: 2024-11-10 07:28:40

深入理解Java虚拟机之垃圾收集二的相关文章

深入理解java虚拟机系列(二):垃圾收集器与内存分配策略

第一篇,点这里  深入理解java虚拟机系列(一):java内存区域与内存溢出异常 先直接上结构图,笔记下一次补上,结构图如下:

深入理解Java虚拟机之垃圾收集一

"生存还是死亡" 如何来判定对象是否存活?针对这个问题书中给出了两种算法,分别是引用计数算法和可达性分析算法 引用计数算法 该算法的思路简单并且易于实现.我们给对象中添加一个引用计数器,当有一个地方引用它时,引用计数器就加一,当引用失效时,计数器减一,当计数器为0时就说明该对象不可能再被引用. 客观的评价,该算法判定效率很高,在很多情况下都是一种不错的算法,但是,至少主流的Java虚拟机并没有采用采用这种算法.原因是该算法无法解决对象之间的循环引用问题. 什么是循环引用呢?笔者认为就是

深入理解Java虚拟机笔记---垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现.Java虚拟机规范中对象垃圾收集器应该如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机所提供的收集器可能会有很的差别,并且一般会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器.下面是Sun HotSpot虚拟机1.6版本Update22包含的所有收集器: 上图中,如果两个收集器之间存在连线,就说明它们可以搭配使用. 1.Serial收集器 Serial收集器是最基本.历史最悠久的收集器,曾经(在J

深入理解Java虚拟机笔记---垃圾收集算法

当对象判定为"已死"状态,虚拟就要采取一定的手段将这些对象从内存中移除,即回收垃圾,回收过程有采用一定的算法.如下是一些主要的垃圾收集算法: 1.标记-清除算法 该算法是最基础的算法,分为"标记"和"清除"两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象.之所有说它是最基础的算法是因为后续的收集算法都是基于这种思路并对其缺点进行改进得到的.它的缺点主要有两个:一个是效率问题,标记和清除过程效率都不高:另外一个是空间问

《深入理解Java虚拟机》——垃圾收集器与内存分配策略

GC需要完成: 哪些内存需要回收 什么时候回收 如何回收 如何确定对象不再使用 引用计数算法 给对象添加一个引用计数器,当有一个地方引用它时,计数器值进行加1操作:当引用失效时,计数器值进行减1操作:当计数器值为0,则说明对象不可能再被使用.但是它无法解决循环引用的问题. public class ReferenceCountingGC { public Object instance = null; public static void testGC(){ ReferenceCountingG

深入理解java虚拟机(3)垃圾收集器与内存分配策略

一.根搜索算法: (1)定义:通过一系列名为"GC Roots"的对象作为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的时候,则证明此对象不可用 (2)GC Roots对象包括这几种:虚拟机栈中引用的对象:方法区中的类静态属性引用的对象:方法区中常量的引用对象:本地方法中JNI的引用对象 二.垃圾收集算法 1.标记-清楚算法: (1)基本思想:首先标记出要回收的对象,在标记完后回收掉所有被标记的对象 (2)缺点:第一是标记和清楚

深入理解Java虚拟机阅读心得(二)

垃圾收集 程序计数器.虚拟机栈.本地方法栈三个区域随线程而生,随线程而灭:这几个区域的内存分配和回收都具备稳定性,不需要过多的考虑回收的问题.而Java堆和方法区则不一样. Java堆中存储了几乎所有的对象实例,垃圾收集器进行对堆的回收之前,需要判断这些对象是否还存活 一.判断对象是否存活 判断对象是否还活着,主要有两种方法 1.引用计数法 给对象添加一个引用计数器,每当一个地方引用时,计数器值加1: 每当一个引用失效时,计数器减1: 任何计数器为0的对象为不可能被使用的对象. 优点:实现简单,

深入理解java虚拟机(十二) Java 语法糖背后的真相

语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这些语法糖虽然不会对语言的功能产生任何影响,却能使程序员更方便的使用语言开发程序,同时增强程序代码的可读性,避免出错的机会.但是如果只是大量添加和使用语法糖,却不去了解他,容易产生过度依赖,从而无法看清语法糖的糖衣背后,程序代码的真实面目. 总而言之,语法糖可以看做是编译器实现的一些"小把戏",这些"小

《深入理解JAVA虚拟机》JDK的垃圾收集算法

概念 垃圾收集是很多使用JAVA语言的IT从业者了解得比较少的地方. 但是涉及性能时非常重要.大公司面试除了算法,这部分也是会经常考察的地方. <深入理解JAVA虚拟机>一书中讲到JVM的垃圾收集算法和垃圾收集器. 垃圾收集算法分为: 1.标记清除算法 通常用在回收老年代内存. 最早的搜集算法就是标记清除(Mark-Sweep)算法了. 其原理是分为标记和清除两个阶段: 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象. 主要不足两个: 一个是效率问题,标记过程和清除过程效率