【浅度渣文】JVM——简述垃圾回收

栏目: Java · 发布时间: 6年前

内容简介:自动垃圾收集是查看堆内存的过程,可以识别哪些对象正在使用,哪些不是,以及删除未使用的对象。一个正在使用的对象或一个被引用的对象,意味着你的程序的某个部分仍然保持着一个指向这个对象的指针。未使用的对象或未引用的对象不再被程序的任何部分引用。所以未被引用的对象所使用的内存可以被回收。在像C这样的编程语言中,分配和释放内存是一个手动过程。在Java中,释放内存的过程由垃圾收集器自动处理。基本过程可以描述如下。这个过程的第一步就是标记。这是垃圾收集器标记内存中哪些对象正在被使用,哪些对象已经没有被使用。

自动垃圾收集是查看堆内存的过程,可以识别哪些对象正在使用,哪些不是,以及删除未使用的对象。一个正在使用的对象或一个被引用的对象,意味着你的程序的某个部分仍然保持着一个指向这个对象的指针。未使用的对象或未引用的对象不再被程序的任何部分引用。所以未被引用的对象所使用的内存可以被回收。

在像C这样的编程语言中,分配和释放内存是一个手动过程。在 Java 中,释放内存的过程由垃圾收集器自动处理。基本过程可以描述如下。

第1步:标记

这个过程的第一步就是标记。这是垃圾收集器标记内存中哪些对象正在被使用,哪些对象已经没有被使用。

【浅度渣文】JVM——简述垃圾回收

有用的对象显示为蓝色,没有用的对象显示为黄色。在标记阶段扫描所有对象,然后做出这个决定。如果必须扫描系统中的所有对象,这可能是非常耗时的过程。

第2步:普通删除

内存维护着一个空闲内存列表,每次分配空间时,会来这个列表上找到合适的空间分配。正常删除时,会把没有用到的对象的内存空间还给空闲列表。

【浅度渣文】JVM——简述垃圾回收

另一种第2步:删除并压缩

为了进一步提高性能,除了删除未引用的对象之外,还可以压缩剩余的引用对象。 通过移动被引用的对象,这使得新的内存分配变得更容易和更快。

【浅度渣文】JVM——简述垃圾回收

为什么使用分代垃圾收集?

如前所述,标记和压缩JVM中的所有对象效率不高。 随着越来越多的对象被分配,对象列表的增长和增长导致更长和更长的垃圾收集时间。 然而,应用程序的实证分析表明,大多数对象是短暂的。

这里给个数据的例子。

【浅度渣文】JVM——简述垃圾回收

正如你所看到的,随着时间的推移对象保持存活的越来越少。 实际上,大多数对象的寿命都很短,如图左侧较高的值所示。

JVM 的分代

根据上面的对象的行为特性,我们可以总结出一个更好的方式来提高JVM垃圾回收的效率。所以,就把堆内存分成几种代, 新生代老年代永久代 (Java8之后就没有永久代了,取而代之的是元数据Metaspace)。

【浅度渣文】JVM——简述垃圾回收

一个新的对象会被分配在 新生代 上,并且新的对象会在新生代里慢慢变老。当新生代的空间被占满后,就会触发一次minor gc。假设新生代里的对象死亡率很高的话,那么新生代的垃圾回收就是很优的。一个充满死亡对象的新生代收集起来其实很快。幸存下来的对象会慢慢变老,直到可以移入老年代。

Stop the World Event——所有的新生代手机都是停止世界的事件。Stop the World Event的意思是,所有的应用程序的线程都会被暂停,直到垃圾回收完成。新生代GC总是Stop the World。

老年代是存放那些经历了多次minor gc,年纪达到一个阈值之后的存活的对象。一般来说,会给对象设置一个年龄阈值,达到阈值之后,就会移入老年代。最后,老年代需要进行垃圾回收,就会触发一次major gc。

Major gc也是导致Stop the World。在大部分情况下,major gc是会比minor gc慢很多。所以,对于一个关注响应时间的应用来说,应该尽可能的降低major gc的次数。这里也要注意到,major gc的停顿时间(Stop the World的时间)是和你选取的垃圾收集器有关的。

永久代包含了JVM所需要的class和method的定义等元数据。永久代会随着JVM运行时加载的class而填充新的元数据。除此之外,Java SE的类库也会被存储在这里。

如果JVM检测到这部分class不会被使用了,而且需要更多的内存空间来加载其他的class,那么class也会被回收(unloaded/卸载)。这个收集包含在一次full gc中。(即便是在Java8之后,没有了所谓了永久代,取而代之的是元数据,但是,也会存在类型卸载的回收)

分代垃圾回收

现在你已经明白了为什么需要把堆细分成不同的几个代,现在是时候仔细的看看这种空间是如何工作的了。下面的图演示了在JVM中,对象的分配和变老的过程。

1.首先,任何对象都会被分配在eden区。两个suvivor区一开始都是空的。

这里是为了给读者介绍垃圾回收器的设计过程,和一步步的思考过程,在之后还是会有很多优化,可能会和一开始的设计意图相违背,请见谅。比如,有的对象甚至不分配到堆里(逃逸分析),有的大对象甚至会直接分配到old区(大对象分配),有的对象甚至会分配到堆外内存(nio等),等等各种特殊情况。

【浅度渣文】JVM——简述垃圾回收

2.当eden满了之后,就会触发一次minor gc。

【浅度渣文】JVM——简述垃圾回收

3.活着的对象会被移到第一个suvovor区(第一个第二个都是相对的)。没有被是用的对象就直接被清除了。

【浅度渣文】JVM——简述垃圾回收

4.下一次minor gc发生时,同样的操作。没有被使用的对象被清除,活着的对象和被移到另一个suvivor区。而且,这些对象年龄会+1,然后被移入第二个suvivor。所有的活着的对象都被移入这个新的suvivor1,那么eden和suvivor0又都空了。但是,现在在suvivor1中,对象的年龄是不一样的。

【浅度渣文】JVM——简述垃圾回收

5.下一次minor gc,又会重复上面的步骤。不过对象是从eden和suvicor1移入到suvivor0中了。

【浅度渣文】JVM——简述垃圾回收

6.终于,随便不断的minor gc,对象的年龄越来越大,达到了阈值(这里是8)时,他们会晋升带老年代。

【浅度渣文】JVM——简述垃圾回收

7.随着更多的minor gc,也有更多的对象晋升到老年代。

【浅度渣文】JVM——简述垃圾回收

8.上面已经涵盖了新生代的整个过程。最后,老年代需要进行一次major gc来清除,压缩老年代的空间。

【浅度渣文】JVM——简述垃圾回收

执行并观察

上面说了那么多,相信机智的读者已经大致了解垃圾回收的过程了。现在让我们亲眼看一看这个执行过程。这一部分,我们会运行一个Java应用程序,然后使用Visual VM分析回收的过程。Visual VM是JDK提供给我们的一个工具,开发者可以使用这个 工具 对JVM进行各个方面的监视。

1. 你需要先去Oracle官网下载JavaDemo。

2.启动示例代码

确保你的电脑已经安装了JDK,并下载了上一步说的demo。然后解压到本地一个目录下。我的目录是 /Users/teeyoung/Desktop/code4me/javademos8

然后执行Java2demo.jar, java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar Java2Demo.jar

注意:1.这些命令稍后会解释;2.-XX:PermSize=20m -XX:MaxPermSize=20m如果是在Java 8之后是提示无效,以为已经被移除了。

程序运行起来是这个样子:

【浅度渣文】JVM——简述垃圾回收

你可以看到很多tab,那些演示了Java的绘图功能(看到这个程序,让我想到了买家秀和卖家秀,别人写的代码和我写的代码)。

随意点击各个tab,大致是这样的:

【浅度渣文】JVM——简述垃圾回收

这个界面可以看到垃圾回收行为的结果,看右下角的内存监视。我们先让他运行着,我们稍后会用到它的。

3.启动VisualVM

如果你的jdk/bin已经在你的path下了,那么直接执行 jvisualvm ,否则需要输入完整的路径,如: /usr/bin/jvisualvm

【浅度渣文】JVM——简述垃圾回收

4.安装Visual GC插件

我们需要安装Visual GC这个插件,但是java.net这个站点都关了,无法联网安装,所以我写了另一篇文章演示如何安装插件。请移步 jvisualvm插件安装的正确姿势(解决网络问题):http://www.dubby.cn/detail.html?id=9061

安装之后就是这个样子:

【浅度渣文】JVM——简述垃圾回收

5.分析Java2Demo

首先双击Java2Demo这个本地进程,或者右击->Open:

【浅度渣文】JVM——简述垃圾回收

然后点击 Visual GC 这个tab:

【浅度渣文】JVM——简述垃圾回收

然后就自由尝试各个tab页,看看每个信息代表JVM的什么指标。还有,你可以尝试着改变Java2Demo上的string和image的数量,看看对垃圾回收有什么影响。

Java垃圾收集器

现在你知道了垃圾回收的基本概念,还有如何去监视JVM的垃圾回收。现在我们来了解Java给我们提供的不同的垃圾回收器,还有我们需要掌握如何使用这些垃圾回收器的命令行。

通用的命令行

这里给出一些通用的命令行,不管你是什么收集器,都会用到的。

选项 描述
-Xms 设置JVM启动时,堆的初始大小
-Xmx 设置堆的最大的容量
-Xmn 设置新生代的容量
-XX:PermSize 设置永久代的初始大小( Java 8以废弃
-XX:MaxPermSize 设置永久代的最大容量( Java 8以废弃
-XX:MinHeapFreeRatio 设置堆最小空闲容量,低于这个阈值就扩容,但是堆总量还是要在Xmx和Xms之间
-XX:MaxHeapFreeRatio 设置堆最大空闲容量,高于这个阈值就收缩,但是堆总量还是要在Xmx和Xms之间

Serial收集器

Serial收集器是客户端默认的收集器。使用Serial收集器,minor gc和major gc都是单线程处理。而且,老年代使用并发-压缩算法。把老年代的活着的对象移到老年代的前面,后面空出空闲区域,以供后续分配,可以避免空间碎片。

使用场景

Serial收集器是一些客户端(PC,不是服务器)应用使用,而且对于低延时要求不高的。他的优势是单线程处理。直到今天,对于一些不是很重要,堆内存只有几百MB的应用来说,Serial GC依然是个很有效的垃圾收集器。

还有一个广泛使用Serial收集器的场景是,一个机器上运行着很多JVM(在某些场景下,JVM的数量比处理器的核数还要多)。在这种情况,使用Serial收集器可以减少JVM之间冲突,即便GC的时间变长了。

最后,随着嵌入式设备的普及,内存少,核数少,Serial收集器可能会重新绽放光彩。

命令行选项

开始Serial收集器:

-XX:+UseSerialGC
复制代码

给个完整的例子:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar Java2demo.jar
复制代码

Parallel收集器

Parallel收集器在回收新生代时,使用多线程进行收集。默认的回收线程数等于机器的核数。可以使用 -XX:ParallelGCThreads=<desired number> 来设置希望的线程数。

在只有一个CPU的机器上,即便你已经开启了Parallel收集器,JVM还是会使用默认的收集器来工作。

使用场景

Parallel收集器也被称为 吞吐收集器 。因为他可以利用多线程来加快应用的吞吐。这个收集器一般被用作有很多工作需要做,而且对低延要求时不那么高的应用。例如,批处理(报表,账单,或者是很大的数据库查询等)。

-XX:+UseParallelGC

这个命令行选项是开启新生代的多线程收集,老年代的多线程收集。老年代也是整理方式。

给个完整的例子:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseParallelGC -jar Java2demo.jar
复制代码

-XX:+UseParallelOldGC

这个选项是开启新生代的多线程收集,老年代的多线程收集。老年代也是整理方式。

整理:就是会把活着的对象移到内存的前面,这样就对象和对象之间的小的空闲的空间(内存碎片)。内存碎片可能导致,空闲空间足够,但是大对象无法分配的情况。

完整的例子:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseParallelOldGC -jar Java2demo.jar
复制代码

CMS收集器

并发(Concurrent)标记(Mark)清除(Sweep)收集器(CMS)(也被叫做:并发低延时收集器)是一个手机老年代的垃圾收集器。他试图把大部分垃圾收集工作和应用程序的线程并发执行,以降低所造成的停顿(Stop the World)时间。通常情况下,CMS不会压缩整理活着对象。所以,会存在内存碎片的问题。如果内存碎片成为你的问题,那么可以考虑换用更大的堆(哈哈,也可以考虑换收集器,但是,换更大的堆是直接并且简单的方法)。

注意:CMS收集器在新生代的收集方式和Parallel在新生代的收集方式一样(单线程,复制)。

使用场景

CMS适用对低延时有高要求的应用。比如,响应事件的桌面应用,响应请求的Web服务器,或者响应查询的数据库。

命令行选项

开启命令:

-XX:+UseConcMarkSweepGC
复制代码

设置线程数:

-XX:ParallelCMSThreads=<n>
复制代码

这里给个完整的例子:

java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseConcMarkSweepGC -XX:ParallelCMSThreads=2 -jar Java2demo.jar
复制代码

G1收集器

具体的可以查看 【浅度渣文】JVM——G1收集器:http://www.dubby.cn/detail.html?id=9059

这里简单描述一下吧,G1在Java 7才出现的,是一个并发的,低延时的,整理收集器。对堆内存管理和之前的收集器都不一样。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro HTML5 and CSS3 Design Patterns

Pro HTML5 and CSS3 Design Patterns

Michael Bowers / Apress / 2011-11-15 / GBP 35.50

Pro HTML5 and CSS3 Design Patterns is a reference book and a cookbook on how to style web pages using CSS3 and HTML5. It contains 350 ready--to--use patterns (CSS3 and HTML5 code snippets) that you ca......一起来看看 《Pro HTML5 and CSS3 Design Patterns》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具