013 GC机制

本文转自:https://www.cnblogs.com/shudonghe/p/3457990.html

最近还是在找工作,在面试某移动互联网公司之前认为自己对Java的GC机制已经相当了解,其他面试官问的时候也不存在问题,直到那天该公司一个做搜索的面试官问了我GC的问题,具体就是:老年代使用的是哪中垃圾回收算法,并详细解释第一步做什么,第二部做什么?这时候才发现具体一步一步怎么来的,确实不知道。那结果就可想而知,面试官就对我不感兴趣了。那一瞬间,感觉自己不应该过分轻信别人的博客,要相信官方的文档,因为有些写博客的技术人员也许自身对某些技术都不是很了解,只是自己记录下学习和使用的经历,再或者文章可能是从别人那里转发而来。所以,写下这篇文章,记录这次惨痛的教训,也希望自己以后学东西能够追根溯源,知其然,更要知其所以然!

本文主要介绍,JVM的组件,自动垃圾收集器是如何工作的,分代垃圾收集器的收集过程,使如何用Visual VM来监视应用的虚拟机,以及JVM中垃圾收集器的种类。

一、JVM架构

1、HotSpot 架构

HotSpot JVM架构支持较强的基本特征和功能,此外还支持高性能和高吞吐率的特性。例如,JVM JIT编译器产生动态优化的代码,亦即,编译器是在Java运行的时候的时候进行优化,并为当然的系统架构生成高性能的目标机器指令。此外,经过对运行时环境和多线程垃圾回收器不断地设计和优化,现在的HotSpot JVM甚至在大型的系统上都具有较高的伸缩性。JVM 的主要组件包括:ClassLoader、运行时数据区和执行引擎。

2、 HotSpot关键组件

与性能密切相关的JVM的关键组件,有堆、JIT编译器,垃圾收集器,在下图中这些组件用深色标注。

性能优化只需要关注这三个组件即可。堆是存储对象的地方,该区域由用户指定(可以在启动应用程序的时候指定)的垃圾回收器来管理。大多数优化选项都是通过配置堆的大小和选择最合适的垃圾回收器来实现。JIT编译器对性能也能产生比较大的影响,但是对于更新版本的(本文档为JDK1.7)JVM很少需要对其进行优化。

二、应用程序性能的衡量要素

通常数来,当优化一个Java应用的时候,我们通常重点关心的是响应时间或吞吐量两者其中的一个。再此对这两个概念做下介绍,便于加深对优化的理解。

1、响应时间

响应时间指的是应用或者系统对一个请求数据的回应。例如:

桌面UI对鼠标事件的响应速度

网站返回页面的速度

数据库查询返回的速度

所以,对于重点关心响应时间的应用,较长时间的应用暂停时不可接受的。我们要做到尽可能的提升响应速度,减少响应时间。

2、吞吐量

吞吐量重点关心特定时间内应用程序处理工作的最大值。例如,吞吐量可以通过以下形式来衡量:

给定时间内的完成的事物数量

一个小时你完成的批处理程序的个数

一个小时内完成的数据库查询的次数

这种情况下,应用程序能容忍较高的暂停时间,因此,高吞吐量的应用程序有更长的时间基准,快速响应是不必考虑的。

三、自动垃圾收集器是如何工作的

1、什么是自动垃圾收集机制?

自动垃圾收集机制是查看堆内存、区分在使用的对象和未使用的对象、删除未使用的对象的一个过程。对于使用对象或者引用对象,指的是你的程序持有一个指向那个对象的引用。对于未使用的对象或者是无引用对象,则不被你程序的任何部分持有引用。所以,无引用对象使用的内存是可以被重新回收利用的。

在类C语言的编程语言中,内存的分配和回收都是手动的。而在Java中,内存的回收是由垃圾回收器自动处理的。基本的步骤可以描述如下:

步骤一:标记

第一步是标记,通过这一步骤来区分哪块内存在使用,那哪块内存未使用。

引用对象用蓝色标识,未引用的对象用金色标识。在标记阶段,扫描所有的对象并判断。如果系统中所有的对象都要被扫描的,那么这一步骤可能非常耗时。

步骤二:正常删除

正常删除移除无引用对象,留下引用对象及指向空闲空间的指针。

内存分配器持有空闲内存的引用,这些空闲内存都链接到一个List中,当需要的时候可以分配给新的对象。

步骤二a:带压缩删除

为了进一步改善性能,除了删除未引用的对象,用户也可以压缩存活的引用对象。把引用对象移动到一起,通过这种方法可以使更快速、更方便的分配新的内存。

2、为什么使用分代垃圾回收机制?

在早期的JVM上,不得不在所有的对象上进行标记-压缩,这显然是非常低效。随着越来越多的对象被分配,对象列表也逐渐增大,这就导致越来越长的垃圾回收时间。然而,根据经验我们分析得到大部分对象的生命周期是非常短暂的。

下面是这些数据的一个例子,Y轴代表的是分配的字节数,X轴代表的是随着时间的推移分配的字节数。

从图中可以看到,随着时间的推移,分配后的对象遗留的越来越少。事实上,大多数对象有非常短的生命周期,从图中左边较高的值得宽度可以得出。

3、JVM各代介绍

从上面对象分配行为中,我们知道据此可以增强JVM的性能。因此,堆被分解为较小的三个部分或者三个代。具体分为:年轻代、老年代、持久代。

年轻代:所有创建的新对象都是在年轻代分配堆空间,在这一代变老。当年轻代被填满的时候,这就会导致一个小收集。如果对象的死亡率很高,小回收就可以获得优化。年轻代中死亡的对象越多,回收的速度也就越快。幸存对象逐渐变老(年纪增大),最终会移动到老年代。

全局暂停事件:所有的小收集都是一个个全局暂停事件。这意味着所有的应用线程都会停止,直到收集操作完成。小回收总会导致全局暂停事件。

老年代:老年代用于存储较长生命周期的对象。典型的说来就是,为年轻代对象设置了阈值,当年轻代逐渐变老,到达这个阈值的时候,对象就会被移动到老年代。随着时间的推移,老年代也会被填满,最终导致老年代也要进行垃圾回收。这个事件叫做大收集。

大收集也是全局暂停事件。通常大收集比较慢,因为它涉及到所有的存活对象。所以,对于对相应时间要求高的应用,应该将大收集最小化。此外,对于大收集,全局暂停事件的暂停时长会受到用于老年代的垃圾回收器的影响。

持久代:持久代存储了描述应用程序类和方法的元数据,JVM运行应用程序的时候需要这些元数据。持久代由JVM在运行时基于应用程序所使用的类产生。此外,Java SE类库的类和方法可能也存储在这里。

如果JVM发现有些类不在被其他类所需要,同时其他类需要更多的空间,这时候这些类可能就会被垃圾回收。

四、垃圾回收的一般步骤

从上面的介绍中我们已经理解了为什么堆被分成不同的代,下面我们就需要更精确的理解这些空间是如何进行交互的。下面的一组图片展示了JVM中垃圾回收的一般过程,从对象分配到对象逐渐变老。

1、首先,所有新生成的对象都是放在年轻代的Eden分区的,初始状态下两个Survivor分区都是空的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。

2、当Eden区满的的时候,小垃圾收集就会被触发。

3、当Eden分区进行清理的时候,会把引用对象移动到第一个Survivor分区,无引用的对象删除。

4、在下一个小垃圾收集的时候,在Eden分区中会发生同样的事情:无引用的对象被删除,引用对象被移动到另外一个Survivor分区(S1)。此外,从上次小垃圾收集过程中第一个Survivor分区(S0)移动过来的对象年龄增加,然后被移动到S1。当所有的幸存对象移动到S1以后,S0和Eden区都会被清理。注意到,此时的Survivor分区存储有不同年龄的对象。

5、在下一个小垃圾收集,同样的过程反复进行。然而,此时Survivor分区的角色发生了互换,引用对象被移动到S0,幸存对象年龄增大。Eden和S1被清理。

6、这幅图展示了从年轻代到老年代的提升。当进行一个小垃圾收集之后,如果此时年老对象此时到达了某一个个年龄阈值(例子中使用的是8),JVM会把他们从年轻代提升到老年代。

7、随着小垃圾收集的持续进行,对象将会被持续提升到老年代。

8、这样几乎涵盖了年轻一代的整个过程。最终,在老年代将会进行大垃圾收集,这种收集方式会清理-压缩老年代空间。

五、JVM垃圾收集器的种类

现在我们已经知道垃圾收集器的一些基本原理,并且借助VisualVM可以观察到垃圾收集器的实时表现。本节将会详细讲解Java可以使用的垃圾回收器,以及在命令行如何选用配置它们。配置JVM有很多可以用的命令行参数,本节选用常用的配置参数进行详细解。

与堆配置相关的参数

Java中有很多可以使用的命令行参数,这一节将会介绍常用的一些命令行参数。

 
参数 描述
-Xms JVM启动的时候设置初始堆的大小
-Xmx 设置最大堆的大小
-Xmn 设置年轻代的大小
-XX:PermSize 设置持久代的初始的大小
-XX:MaxPermSize 设置持久代的最大值

1、串行收集器:

在Java SE 5和6中,串行收集器是客户端环境(client-style machines)机器的默认设置。在这种情况下,小垃圾收集和大垃圾收集都是串行进行的(使用单个的虚拟CPU)。

使用的算法说明:

串行收集器在年轻代使用的是拷贝算法,这个算法比较简单,在这里不做详述。而年老代和持久代使用标记-清扫-压缩(mark-sweep-compact)算法。标记阶段,收集器识别哪些对象仍然活着。清扫阶段“扫荡”整个代,识别垃圾。之后,收集器执行平移压缩(sliding compaction),将存活的对象平移到代的前端(持久代类似),相应的在尾部留下一整块连续的空闲空间。压缩后,以后的分配就可以在年老代和持久代使用空闲指针(bump-the-pointer)技术。这种压缩算法能够在堆上迅速分配内存块。

示例:大多数客户端式(client-style machines)机器上运行的应用程序通常都是选择串行收集器,这些应用对短暂停没有要求。它之所以叫这个名字,是因为它能充分利用单个虚拟处理器进行垃圾回收的工作。在今天的硬件上,串行收集器可以有效的管理许多拥有几百M堆内存的重要应用程序,并且拥有相对短的最坏暂停(Full GC仅有几秒左右)。

在有大量JVM运行在同一个机器上(在某些情况下,JVM的个数比可以用的处理器的个数多)的应用环境下,串行垃圾收集器也被广泛使用。在这种环境下,要进行垃圾回收的JVM最好使用一个处理器,虽然这样会使垃圾回收的时间变得更长,但可以降低与其他JVM的冲突。这时,使用串行垃圾回收器能够获得很好的权衡。最后,如果在较小的内存和较少的CPU核心上对硬件进行稍加扩充,将能获得更好的性能。

命令行参数

使用串行垃圾回收器 -XX:+UseSerialGC

给事例应用使用串行垃圾回收器的命令行如下:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar
c:\javademos\demo\jfc\Java2D\Java2demo.jar

2、并行收集器

并行垃圾收集器在年轻代使用多线程进行垃圾回收。默认情况下,在N个CPU的主机上,并行垃圾收集器使用N个垃圾收集器线程进行垃圾回收。垃圾收集器线程的个数可以在命令行进行设置:-XX:ParallelGCThreads=<期望的数值>

在单核的CPU上,尽管我们请求设置的是并行垃圾收集器,但JVM还是使用默认的垃圾收集器。在两个CPU的主机上,并行垃圾收集器与默认的串行垃圾收集器所表现出来的性能相当,年轻代的垃圾收集器暂停时间与两个以上CPU的主机相比也有所减少。并行垃圾收集器有两种使用方式。

使用的算法说明:

年轻代:与串行垃圾收集器年轻代相同的拷贝算法,只不过是该算法的并行版本,使用多个CPU并行的运行,减少了垃圾收集的开销,因此增加了吞吐量。

年老代:与串行垃圾收集器老年代想听的标记-清扫-压缩(mark-sweepcompact)算法,只不过是该算法的并行版本。

示例并行收集器也叫做吞吐量收集器,因为其可以使用多个CPU来增大应用程序的吞吐量。当应用程序需要处理大量的工作同事可以接受较长的暂停时,可以使用并行垃圾收集器。例如,想打印报告或者账单这样的批处理,或者进行大量的数据库查询。

-XX:+UseParallelGC

使用这个命令行参数,就会将年轻代设置为多线程的收集器,老年代使用单线程的收集器。该选项,还会在老年代进行单线程的压缩工作。

启动示例应用程序Java2Demo的命令行如下:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseParallelGC -jar
c:\javademos\demo\jfc\Java2D\Java2demo.jar

-XX:+UseParallelOldGC
使用该参数,年轻代和老年代都会使用多线程的收集器,同时,也使用多线程的压缩收集器。HotSpot仅仅在老年代进行整理,在年轻代是一个复制收集器,因此没必要进行整理。

压缩描述的是这样一种行为,移动对象使得个对象之间没有空闲位置。再一次垃圾收集的清理之后,存活对象在内存中的存储位置之间可能存在空闲区。整理移动对象,使得对象的存储都是顺序的,彼此之间没有空闲区。垃圾收集器可能也是一个不带压缩的收集器。所以,并行收集器和并行压缩收集器之间的区别就是后者在垃圾收集清理操作之后,对内存空间进行一次整理。

启动示例应用程序Java2Demo的命令行如下:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseParallelOldGC -jar
c:\javademos\demo\jfc\Java2D\Java2demo.jar

3、并发标记清理收集器

并发标记清理收集器(CMS,又叫作并发低暂停收集器)在老年代进行收集。由于垃圾收集能使用应用线程的并发进行大多数的垃圾收集工作,所以它降低了应用程序的暂停时间。

正常说来,并发低暂停的收集器对存活对象不进行复制和压缩的工作。这种情况下,垃圾收集器没有移动任何存活对象。如果因此而带来了内存的碎片问题,那就为其分配一个更大的堆。

注意CMS收集器在年轻代使用和并行收集器一样的算法。

示例:CMS收集器常常应用于需要低暂停及可以与垃圾收集器共享资源的场景。例如:桌面UI应用程序对事件的响应,Web服务器对请求的响应,以及数据库对查询请求的响应。

命令行参数

如果要使用CMS收集器,使用 -XX:+UseConcMarkSweepGC ,同时,可以设置并发的线程数目 -XX:ParallelCMSThreads=<n> 。

启动示例应用程序Java2Demo的命令行如下:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseConcMarkSweepGC -XX:ParallelCMSThreads=2 -jar c:\javademos\demo\jfc\Java2D\Java2demo.jar

4、G1 垃圾回收器

在Java 7中可以使用G1垃圾回收器,它设计的初衷是用于长期取代CMS收集器。G1垃圾收集器是一个并行、并发,同时也是基于增量整理的低暂停垃圾收集器。与前面所描述的垃圾收集器相比,从布局方面与它们有很大的不同。但本文不对该部分做详细的说明,有兴趣可以参考具体的文献资料。

命令行参数:

如果要使用CMS收集器,使用 -XX:+UseG1GC

启动示例应用程序Java2Demo的命令行如下:

java -Xmx12m -Xms3m -XX:+UseG1GC -jar c:\javademos\demo\jfc\Java2D\Java2demo.jar

六、总结

本文对Java中的JVM进行了较为详细的介绍。我们知道了堆和垃圾收集器在Java JVM中是非常重要的部分。自动的垃圾收集是通过分代垃圾收集的方法来完成的。一旦我们知道了这一原理,我们就可以通过Visual VM虚拟机工具来观察整个过程。

原文地址:https://www.cnblogs.com/mu-tou-man/p/10426876.html

时间: 2024-10-11 07:09:39

013 GC机制的相关文章

【转载】GC基本算法及C++GC机制

原文: GC基本算法及C++GC机制 阅读目录 前言 基本概念 有向可达图与根集 三种基本的垃圾收集算法及其改进算法 1.引用计数算法 2. Mark & Sweep 算法 3. 节点复制算法 分代回收 C++垃圾回收机制 参考书籍 正文 回到顶部 前言 垃圾收集器是一种动态存储分配器,它自动释放程序不再需要的已分配的块,这些块也称为垃圾.在程序员看来,垃圾就是不再被引用的对象.自动回收垃圾的过程则称为垃圾收集(garbage collection).在一个支持垃圾收集的语言中,程序显式地申请内

Java 内存区域和GC机制

目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对 JVM(Java Virtual M

Java内存区域划分和GC机制

Java 内存区域和GC机制 目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对 JVM

Java 内存区域和GC机制-java概念理解

推荐几篇关于java内存介绍的文章 Java 内存区域和GC机制 http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html Jstatd方式远程监控Linux下 JVM运行情况 http://www.aiuxian.com/article/p-3218924.html 用Zabbix通过JMX方式监控weblogic http://www.huilog.com/?p=688 消息队列 http://kb.cnblogs.com

深入JVM系列(二)之GC机制、收集器与GC调优(转)

一.回顾JVM内存分配 需要了解更多内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配 1.1.内存分配: 1.对象优先在EDEN分配2.大对象直接进入老年代 3.长期存活的对象将进入老年代 4.适龄对象也可能进入老年代:动态对象年龄判断 动态对象年龄判断: 虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,当Survivor空间的相同年龄的所有对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代

Java GC机制

Java中GC机制(garbage collection)是垃圾回收机制,更确切的说是内存回收机制 在一个对象不再被程序引用时,它所占用的堆空间就可以回收,以便分配给新对象使用.而且除了释放不再被引用的对象外,垃 圾收集器还要处理堆碎块(堆碎块是在正常的程序运行时产生的),因为新的对象分配了空间,不再被引用的对象被释放,所以堆内存的空闲位置介于活对的对象之间,而请求分配新对象时可能不得不增大堆空间,因为虽然总的空闲空间是够的,但堆中没有连续的空闲空间放得下新对象. 上面是垃圾收集的作用,其好处在

Java 内存区域和GC机制(转)

Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对 JVM(Java Virtual Machine)中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息(Nerver Stop

Java内存区域和GC机制篇

Java内存区域和GC机制一.目录 1.Java垃圾回收概括 2.Java内存区域 3.Java对象的访问方式 4.Java内存访问机制 5.Java GC 机制 6.Java垃圾收集器 二.Java垃圾回收概括 1.Java GC 介绍: a) Garbage Collection 垃圾收集.垃圾回收机制: b) Java中不需要编写内存回收和垃圾清理代码,也不需要考虑内存泄漏和溢出的问题: c) 因为在Java虚拟机中存在自动内存管理和垃圾清理机制: d) 该机制会对JVM(Java 虚拟机

从C#垃圾回收(GC)机制中挖掘性能优化方案

GC,Garbage Collect,中文意思就是垃圾回收,指的是系统中的内存的分配和回收管理.其对系统性能的影响是不可小觑的.今天就来说一下关于GC优化的东西,这里并不着重说概念和理论,主要说一些实用的东西.关于概念和理论这里只做简单说明,具体的大家可以看微软官方文档. 一.什么是GC                                                                                              GC如其名,就是垃圾收集