“阿里架构师”的JVM之GC详解

GC的概念

Grabage Collection:在系统运行过程中占据空间的无用对象在一定时间范围内被及时清理来保证整个系统有足够的内存空间来运行。java中GC的对象是堆和永久区。


常用的GC算法

引用计数法(reference counting)

概念:对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,则对象A就不可能再被使用。

出现的问题:①.引用和去引用伴随加法和减法,影响性能。②.很难处理垃圾对象的循环引用。下面一段代码说明循环引用问题:


packagejvm;/** * GC引用计数法Demo * @author Administrator * */publicclass JvmGcDemo {public static void main(String[] args) { GcObject g1 =newGcObject();//栈中创建变量g1指向(引用)new出对象GcObject1(堆中),此时GcObject1的引用计数为1 GcObject g2 = new GcObject();//栈中创建变量g2指向(引用)new出对象GcObject2(堆中),此时GcObject2的引用计数为1 g1.o = g2;//GcObject1的引用计数再加1,引用计数=2 g2.o = g1;//GcObject2的引用计数再加1,引用计数=2 g1 = null;//GcObject1的引用计数减1,引用计数=1 g2 = null;//GcObject2的引用计数减1,引用计数=1 } } class GcObject{ public Object o; }1234567891011121314151617181920

看上面代码流程发现GcObject1和GcObject2循环引用,引用计数都为1,最后引用计数法无法回收这两个无用的对象。

在eclipse中初始化配置jvm参数,最大使用内存20M,最小使用内存5M:-Xmx20m -Xms5m;修改上段代码,用System.gc()进行GC,即下面代码:


package jvm;/** * GC引用计数法Demo * @author Administrator * */publicclass JvmGcDemo { public static void main(String[] args) { printMemory(1);//打印内存 free memory:4.719520568847656M,约5M GcObject g1 = new GcObject();//栈中创建变量g1指向(引用)new出对象GcObject1(占用1M堆内存),此时GcObject1的引用计数为1 GcObject g2 = new GcObject();//栈中创建变量g2指向(引用)new出对象GcObject2(占用1M堆内存),此时GcObject2的引用计数为1 printMemory(2);//打印内存发现free memory:2.7194900512695312M = 4.719520568847656M - 2M g1.o = g2;//GcObject1的引用计数再加1,引用计数=2 g2.o = g1;//GcObject2的引用计数再加1,引用计数=2 g1 = null;//GcObject1的引用计数减1,引用计数=1 g2 = null;//GcObject2的引用计数减1,引用计数=1 System.gc();//进行GC printMemory(3);//free memory3:4.922454833984375M 说明GcObject1和GcObject2被回收 } /** * 打印内存 */ public static void printMemory(int i){ //打印空闲堆内存 System.out.println("free memory"+i+":"+Runtime.getRuntime().freeMemory()/1024.0/1024+"M"); System.out.println("---------------------------->"); } }class GcObject{ public Object o;byte[] by = new byte[1024*1024];//占用1M内存}1234567891011121314151617181920212223242526272829303132

控制台输出:

free memory1:4.75958251953125M

—————————->

free memory2:2.7194137573242188M

—————————->

free memory3:4.922454833984375M

—————————->

发现GC后free memory3比free memory2增加了2M多一点(多出的部分是回收的其他垃圾内存),free memory3与free memory1无明显差别,说明对象GcObject1和GcObject2被回收,这是因为JVM没有采用引用计数法进行回收垃圾。

标记-清除算法(mark-and-sweep)

概念:标记-清除算法是现代垃圾回收算法的思想基础。是指在可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,进行标记-清除算法将垃圾回收,过程分为两个阶段:标记阶段和清除阶段。在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。

出现的问题:①.标记和清除过程效率不高 ,而且在进行GC的时候,需要停止应用程序,这会导致用户体验非常差劲。②.标记清除之后会产生大量不连续的内存碎片。失效对象都是随即的出现在内存的各个角落的,现在把它们清除之后,内存的布局自然会乱七八糟。

标记-压缩算法(mark-compact)

概念:标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端,之后,清理边界外所有的空间。

出现的问题:标记-压缩算法的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。

复制算法(copying)

概念:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收.

出现的问题:用空间换取时间,空间浪费,不适用于存活对象较多的场合,如老年代等。

分代收集算法(generational collecting)

概念:把对象分为年轻代、年老代、持久代,对不同的生命周期使用不同的算法进行回收,一般新生代对象和小对象适合复制算法,老年代对象和大对象存活适合标记-清理或者标记-压缩算法。

对象的可触及性

可触及的

从根节点可以触及到这个对象。根一般为栈中引用的对象、方法区中静态成员或者常量引用的对象(全局对象)、JNI方法栈中引用对象。

可复活的

一旦所有引用被释放,就是可复活状态,因为在finalize()中可能复活该对象。

不可触及的

在finalize()之后,可能会进入不可触及状态,不可触及的对象不可能复活,可以回收。


GC参数

串行收集器

最古老、最稳定、效率高的收集器,缺点是可能会产生较长时间的停顿,她只使用一个线程去回收,没有办法发挥多核计算机的性能。

-XX:+UseSerialGC

新声代和老年代使用串行回收,新生代使用复制算法、老年代使用标记-压缩算法。

并行收集器

ParNew

-XX:UserParNewGC

新生代使用并行回收,老年代使用串行回收。

新生代的的并行回收采用复制算法,多线程回收(多核支持),可以使用XX:ParallelGCThreads限制线程的数量。当然,多线程并不一定快。

Parallel

类似于ParNew,新声代采用复制算法、老年代使用标记-压缩算法,更加关注吞吐量。

-XX:+UseParallelGC 使用Parallel收集器和老年代串行。

-XX:+UserParalleOldGC 使用Paralle收集器老年代并行。

-XX:MaxGCPauseMills 最大停顿时间,单位毫秒,GC尽力保证收回时间不超过设定的值,但不是肯定。

-XX:GCTimeRatio 在0-100中取值,代表垃圾收集时间占总时间的比,默认99,代表最大允许1%的时间做GC。

CMS收集器

Concurrent Mark Sweep 并发(与用户线程一起执行)标记清除,这是一个老年代的收集器(新声代使用的是ParNew),GC中应用线程停顿时间比较短,所以并发阶段会降低吞吐量。

-XX:+UseConcMarkSweepGC

CMS收集器GC过程

初始标记

根可以直接关联到的对象,速度快

并发标记(和用户线程一起)

主要标记过程,标记全部对象

重新标记

由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正

并发清除(和用户线程一起)

基于标记结果,直接清理对象


CMS收集器特点

尽可能降低停顿

会影响系统整体吞吐量和性能。比如,在用户线程运行过程中,分一半CPU去做GC,系统性能在GC阶段,反应速度就下降一半。

清理不彻底。因为在清理阶段,用户线程还在运行,会产生新的垃圾,无法清理。

不能在空间快满时再清理,因为和用户线程一起运行。-XX:CMSInitiatingOccupancyFraction设置触发GC的阈值,如果不幸内存预留空间不够,就会引起concurrent mode failure。

CMS收集器常用参数

-XX:+UseCMSCompactAtFullCollection Full GC,进行一次整理,整理的过程是独占的,停顿时间较长。

-XX:+CMSFullGCsBeforeCompaction 设置几次 Full GC后进行一次一次碎片整理。

-XX:ParallelCMSThreads 设定CMS的线程数量,一般约等于可用CPU的数量,不宜设置太大。

GC的参数整理

-XX:+UseSerialGC:在新生代和老年代使用串行收集器

-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例

-XX:NewRatio:新生代和老年代的比

-XX:+UseParNewGC:在新生代使用并行收集器

-XX:+UseParallelGC :在新生代使用并行回收收集器

-XX:+UseParallelOldGC:老年代使用并行回收收集器

-XX:ParallelGCThreads:设置用于垃圾回收的线程数

-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器

-XX:ParallelCMSThreads:设定CMS的线程数量

-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发

-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理

-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩

-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收

-XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收

-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收

在此我向大家推荐一个架构学习交流群。交流学习群号: 744642380, 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良

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

推荐阅读更多精彩内容

  • 原文阅读 前言 这段时间懈怠了,罪过! 最近看到有同事也开始用上了微信公众号写博客了,挺好的~给他们点赞,这博客我...
    码农戏码阅读 5,946评论 2 31
  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,527评论 3 83
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供...
    简欲明心阅读 89,360评论 17 311
  • JVM架构 当一个程序启动之前,它的class会被类装载器装入方法区(Permanent区),执行引擎读取方法区的...
    cocohaifang阅读 1,644评论 0 7
  • Java 虚拟机有自己完善的硬件架构, 如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM 屏蔽了与具体操作系...
    尹小凯阅读 1,678评论 0 10