JVM垃圾回收之前世今生

本文将带领大家一起来了解JVM垃圾回收(Garbage Collection)的前世今生,希望对初学者有所帮助。

JVM

首先用一张图简单的描述JVM的架构:

JVM的运行时数据区分为线程私有(图中的白色部分)和数据共享区域(图中的黄色部分),而JVM的GC就是针对Heap空间进行的。

说明:文中如未特殊说明,默认是使用HotSpot JVM进行说明。

关于性能

我们研究学习GC机制的主要目的就是为了是我们开发的应用程序具有更高的性能,而根据具体应用程序需求的不同可以将对性能的需求大致分为两方面:

  • 更快的响应时间 -- Responsiveness
  • 更大的吞度量 -- Throughput

例如在浏览网页时,我们需要更快的响应时间;而更大的吞吐量则表现在需要在特定时间内处理尽可能多的任务,所以我们需要根据实际项目的需求来分析出我们到底是需要更快的响应时间还是更大的吞吐量。

为什么要进行垃圾回收?

在C语言中,我们需要手动的分配和释放内存,而在Java中我们使用JVM的垃圾回收机制自动且动态的为我们释放堆内存(Heap),即删除掉Dead状态的对象(我们的应用程序中不在持有该对象的引用)并保留Alive状态的对象(我们的应用程序中仍然有持有该对象引用的地方),如果Heap中的对象越来越多就会造成OOM(内存溢出)并中断我们应用程序的正常运行,所以对Heap进行垃圾回收是必须的,并且垃圾回收的性能将直接影响应用程序的性能。

垃圾回收的方法

Marking

首先GC会将会对Heap中的对象进行扫描(扫描所有的objects将会是一项非常耗时的操作),并将Dead状态的对象所占有的内存区域进行标记(黄色部分),如下图所示:

傻傻的删除操作

标记完成后,GC就会将标记的内存区域中的对象回收并清理,清理完成后Memory Allocator会持有这些空闲部分引用的list,如果有新的内存分配请求Memory Allocator会查找出一块满足要求的内存进行分配。之所以称这种方法为“傻傻的删除操作”就是因为会产生很多的内存碎片,就类似我们的磁盘碎片一样,而且Memory Allocator每次分配内存的时候都需要找出一块能够满足需求的内存,更槽糕的是如果没有能够找到满足需求的内存那就悲剧了。

Compacting

为了提高性能,诞生出了Compacting的方法,即现将标记的部分删除,然后将Referenced objects占用的内存移动到一起(Compacting),Memory Allocator会持有空闲部分开始的引用。这样下次分配操作就变得方便和快速,但是当对象逐渐增多的时候,Compacting操作又会占用过多的时间。

JVM Generations

为了继续提高GC的性能,heap被分为了几个部分,即我们通常所说的generations。具体分为:Young Generation(年轻代)、Old Generation(老年代)和Permanent Generation(永久代,Java8提出了MetaSpace),我在这里举一个更能吸引大家的例子:将GC比喻成媒婆,Young Generation中的对象比喻成一个情窦初开的美女,而到Old Generation时刚才的美女变成了大龄剩女,而Permanent Generation中的元数据信息代表了女方的家人们(主要存储的是被应用程序使用的classes、Java SE library classes和methods的元数据信息),他们都一直在背后默默地支持女方。(这里需要说明一下,举这个例子只为方便大家理解,没有不尊重女性的意思,希望所有的女性朋友都能够找到自己的如意郎君!)

而Yong Generation分成了三个部分:eden、S0、S1,下面我们来描述一下它们各自的作用:

在程序运行时不断有新的objects被分配到eden中,即这里都是情窦初开的美女,当eden部分被填满时,就会引发minor garbage collection,在这个阶段应用程序暂停,直到完成回收,下面我们用几幅图来描述这个过程:

上图说明:当Eden被填满后,执行minor garbage collection,对Unreferenced objects进行标记,然后将Referenced objects复制到S0,而被标记的就被回收和清理(很多美女找到了如意郎君),当然被标记的objects越多,执行minor garbage collection的效率就会越高。

上图说明:当瑕疵minor garbage collection的时候,会将Eden和S0中未被标记的objects(依然没有找到男朋友的美女)复制到S1,并且年龄增加了1,被标记的当然就是找到如意郎君的。

上图表示又进行了一次minor garbage collection,然后一直重复此过程。

当S0或者S1中的一些Unreferenced objects达到一定年龄(美女的年龄超多了指定的年龄)就会被传入到Old Generation(情窦初开的美女编程了大龄剩女),而以上的这些步骤都属于minor garbage collection。

同时,在Old Generation的objects也会被执行垃圾回收,即major garbage collection,即大龄剩女最终找到了如意郎君。

下面我们通过监控工具来向大家动态的展示这个过程。

动起来

首先我们要准备JDK及其Demo:下载地址

然后使用如下命令运行Java2demo这个例子:(注意路径改成自己的,详细的参数会在后面进行解释)

java -Xmx100m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar /Users/mac/Documents/jdkexample/demo/jfc/Java2D/Java2Demo.jar

运行成功会出现以下界面:(这里我使用的是Mac系统)

然后我们点击Transforms部分,显示如下:

然后我们使用jvisualvm命令启动Visual VM:

然后我们选择Tool--Plugins,在Available Plugins中选择Visual GC并点击Install进行安装,安装好后可以在Installed中看到Visual GC:

然后我们在Java VisualVM左侧的Applications列表中选择启动的Java2Demo,右键点击选择打开,在右边就可以看到Java2Demo运行的基本信息:

然后选择Visual GC,就会看到动态的GC效果:

可以看到图中的Eden、S0、S1的动态效果和我们上面的描述一致。

Garbage Collectors

首先我们看一下运行上述例子使用到的一些基本配置参数:

参数 描述
-Xms JVM运行的初始的Heap的大小
-Xmx 最大的Heap大小
-Xmn Yong Generation所占的大小
-XX:PermSize 设置初始的Permanent Generation的大小
-XX:MaxPermSize 设置Permanent Generation的最大尺寸

GC的几种方式

下面我们介绍几种GC的方式:

Serial GC

Serial GC是在Java SE 5和6中使用的,minor和major GC都是连续的执行且使用一个虚拟的CPU,内部使用的是我们在上面讨论过的mark-compact的方法。该方式通常适用于不要求响应速度且运行在客户端的机器,如果要使用Serial Collector可以使用如下配置参数:

-XX:+UseSerialGC

Parallel GC

Parallel GC使用多条线程来执行minor garbage collection,可以使用如下配置设置使用的线程数:

-XX:ParallelGCThreads=<desired number>

Parallel GC可以缩短minor garbage collection的执行时间,进而缩短应用程序的暂停时间。通常Parallel GC用来处理对吞吐量需求比较高的应用,可以使用如下配置参数使用Parallec GC:

-XX:+UseParallelGC

使用这个配置参数,minor部分会使用多个线程,而major部分使用一个线程。

-XX:+UserParallelOldGC

使用这个配置参数,minor和major都会使用多个线程进行处理,并且是多线程执行compacting操作。HotSpot JVM仅仅在major部分使用compacting,而minor部分则使用copy操作。

Concurrent Mark Sweep(CMS)Collector

该方式通常应用在对响应速度要求较高的应用中,minor部分使用的和和Parallel方式相同,即都是copy的方式,而major部分是和应用程序并行执行的,所以该方式只需要应用程序暂停很短的时间。使用CMS方式需要进行如下配置:

-XX:+UseConcMarkSweepGC  #开启CMS的方式
-XX:ParallelCMSThreads=<n>  #使用的线程数

G1 Garbage Collector

G1 Garbage Collector是Java 7开始提出的一种不同于以上几种实现方式的全新垃圾回收机制,我们会用另一篇文章单独进行说明,如果我们想使用这种方式可以使用如下配置参数:

-XX:+UseG1GC

本文我们简单的图像表示方法分析了垃圾回收机制的流程和几种实现方式,并使用Java VisualVM动态的展示了其过程,希望给初学者一些引导和灵感。

本文为原创,欢迎转载,转载请注明出处、作者,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容