JVM GC整理

堆内存模型

几种GC

  • Minor/Young GC: 只收集 Young区的 GC
    每进行一次Young GC,Survivor区内活跃对象将加一岁,达到一定年龄将移入OLD区

  • Old GC: 只收集 Old区的 GC,只有垃圾收集器 CMS 的 concurrent collection 是这个模式

  • Full GC: 收集整个堆,包括新生代,老年代,永久代(JDK1.8及以后为 metaspace 元空间)等所有部分的模式

  • Mixed GC: 收集整个 Young Gen 以及部分 old gen 的 GC,只有垃圾收集器 G1 有这个模式

GC时会stop the world

1、避免无法彻底清理干净
2、GC的工作必须在一个能确保一致性的快照中进行

申请内存大致过程

1、创建对象后,将尝试在Eden申请一片内存
2、若Eden区内存空间足够,则申请结束
3、若Eden区内存空间不足,进行YoungGC(触发清理YOUNG区不活跃对象,活跃对象满足一定条件的晋升到OLD区)
4、若OLD的空间不足(触发阈值或新晋对象等行为导致不足)则触发FullGC
5、若FullGC后,Eden区仍不能满足申请的内存,则触发OOM(out of memory)

GC收集算法(GC收集器都是基于这些算法来实现)

复制(Copying)算法

YoungGC基本都是基于该算法,所以直接以YoungGC的过程来简述该算法

假定正在使用的是S0区,S1区是空闲的
将YOUNG区中活跃的对象复制到S1区(两个Survivor区交替使用,永远有一个是空的,留给下次复制)并清理Eden区和S0区的对象。若活跃对象大于S1区时,部分对象将会直接进入OLD区,部分仍留在S1区
缺点:浪费内存
优点:无内存碎片,对象存活率低时效率高于另外几种
适用:对象存活率低的

标记-清除(Mark-Sweep)算法

从根开始标记存活的对象,然后重新扫一遍将未标记的对象清除同时清除标记
缺点:
产生大量内存碎片,容易导致大内存对象无足够空间,提前触发GC
效率低(全堆对象遍历)stop the world的时间比较长

标记-整理(Mark-Compact)算法

从根开始标记存活的对象,然后整理到一端,其余清除
缺点:效率低(全堆对象遍历与内存整理)
优点:有效利用内存无碎片

年轻代一般适用复制算法,年老代一般适用标记-整理算法

GC的几种收集器

GC收集器-有连线的两种收集器可搭配使用

年轻代收集器

Serial

这是最早的新生代收集器,也是jdk1.5之前默认的收集器。它是基于复制算法实现的,单线程,需要stop the world,所以新生代不能太大,否则对于停顿来讲是比较影响交互响应的。
-XX:+UseSerialGC 使用该收集器

Parallel New

-XX:ParallelGCThreads=NNN 来指定 GC 线程数。 其默认值为CPU内核数。

这是对单线程的Serial的一种改进,ParNew收集器是并行的,在多CPU的场景下会有比串行收集器更好的性能,除此之外,实现算法跟Serial完全一样。需要注意的是,如果CPU数量为1个或2个,该种收集器的性能并不会比Serial要好,因为线程之间的切换会产生额外的开销

Parallel Scavenge

-XX:MaxGCPauseMillis
控制垃圾收集最大停顿时间(毫秒),收集器将尽力保证内存回收花费的时间不超过设定值(设置过小会导致新生代空间变小,导致GC更频繁,总GC时间反而变长、吞吐量下降)

-XX:GCTimeRatio
设置吞吐量大小(0-100),GC时间 <= 1 / (1 + 吞吐量) * 的系统运行花时间。(-XX:GCTimeRatio=99,表示GC时间不超过总时间的1%)

-XX:+UseAdaptiveSizePolicy
自适应策略开关开关参数,不需要手工指定(-Xmn,-XX:SurvivorRatio、-XX:PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量

采用的也是复制算法,它与前两种收集器最大的区别是,它关注的是吞吐量而不是延迟。也被称为是吞吐量优先的收集器。其中,吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
主要使用场景:主要适合在后台运算而不是太多交互的任务,高吞吐量则可以最高效率的利用CPU时间,尽快的完成程序的运算任务

年老代收集器

Serial Old

这个是jdk1.2以前的默认收集器,使用的是【标记-整理】算法,单线程,stop the world

Parallel Old

Parallel Scavenge的老年代版本,使用的是【标记-整理】算法。目前它跟Parallel Scavenge的配合是吞吐量优先场景的优先选择。

CMS

-XX:+UseCMSCompactAtFullCollection,应用于在FULL GC后再进行一个碎片整理过程
-XX:+CMSFullGCsBeforeCompaction,多少次不压缩的full gc后来一次带压缩的
-XX:+UseCMSInitiatingOccupancyOnly=true,使用手动指定回收百分比
-XX:CMSInitiatingOccupancyFraction=92,对内存占用率达到92%提前GC(因为CMS产生浮动垃圾)

CMS,Concurrent Mark Sweep,这是一款真正的并发收集器。前面的都是讲的并行。使用的是【标记-清除】算法
收集过程:
1、初始标记,stop the world,只做GC Root可达性的初始标记
2、并发标记,并发执行,进行GCRoots Tracing(可达性分析)过程,时间很长
3、重新标记,stop the world,标记第二步中变动的对象,时间较长
4、并发清除,并发执行,回收内存空间

缺点:
内存碎片,提前触发一次full gc
产生浮动垃圾,由于清除是并发的,这时用户产生的垃圾无法在本次收集过程中收集掉

收集器 G1

参考:
详文
官方
详文

-XX:+UseG1GC,使用G1收集器
-XX:G1HeapRegionSize,一个Region的大小2的幂
-XX:MaxGCPauseMillis,期望GC停顿时间(毫秒)
-XX:ParallelGCThreads,并行STW的线程数(与逻辑处理器的数量相同)
-XX:ConcGCThreads,并行标记的线程数(ParallelGCThreads的1/4)
-XX:InitiatingHeapOccupancyPercent=60,YoungGC后当整个堆占用超过60%时,就会触发并发GC周期
-XX:G1HeapWastePercent=10,并发标记后可回收的垃圾占比,小于则不回收,10%
-XX:G1MixedGCCountTarget=8,一个周期内触发Mixed GC最大次数,8次
-XX:G1NewSizePercent=35,新生代比例下限,35%
-XX:G1MaxNewSizePercent=60,新生代比例上限,60%
-XX:MetaspaceSize=256m

G1收集器将内存划分了多个区域(Region)
区域的类型有Eden,Survivor,Old,Humongous,除了Humongous外,其余和之前介绍的一样意思
Humongous:用于存放巨型对象(超过Region大小的50%),若一个存不下将找多个连续的Humongous一起。一开始直接存在Old的空间,但G1有优化会在YoungGC对其进行回收(从1.8的某个版本开始)

  • YoungGC
    回收Eden、Survivor、Humongous
    由于会回收Humongous,所以Old的内存空间会有减少,但并不是回收了Old对象
    调整eden与old的占比

  • MixedGC
    每当YoungGC后会根据JVM参数(IHOP等)判断当前是否需要开始进行MixedGC

    MixedGC主要可以分为两个阶段:
    1、全局并发标记(global concurrent marking)

    全局并发标记又可以进一步细分成下面几个步骤:

    • 初始标记(initial mark,STW)。它标记了从GC Root开始直接可达的对象。初始标记阶段借用young GC的暂停,因而没有额外的、单独的暂停阶段。
    • 并发标记(Concurrent Marking)。这个阶段从GC Root开始对heap中的对象标记,标记线程与应用程序线程并行执行,并且收集各个Region的存活对象信息。过程中还会扫描上文中提到的SATB write barrier所记录下的引用。
    • 最终标记(Remark,STW)。标记那些在并发标记阶段发生变化的对象,将被回收。
    • 清除垃圾(Cleanup,部分STW)。这个阶段如果发现完全没有活对象的region就会将其整体回收到可分配region列表中。 清除空Region。

    2、拷贝存活对象(Evacuation)

    Evacuation阶段是全暂停的。它负责把一部分region里的活对象拷贝到空region里去(并行拷贝),然后回收原本的region的空间。Evacuation阶段可以自由选择任意多个region来独立收集构成收集集合(collection set,简称CSet),CSet集合中Region的选定依赖于上文中提到的停顿预测模型,该阶段并不evacuate所有有活对象的region,只选择收益高的少量region来evacuate,这种暂停的开销就可以(在一定范围内)可控

  • 错误的设置将会导致无法按期望的进行MixedGC,从而导致FullGC的出现

注意IHOP、G1MaxNewSizePercent、G1NewSizePercent的关系
eden的占比要小于1-IHOP才能触发MixedGC
(G1NewSizePercent ~ G1MaxNewSizePercent) < 1 - IHOP

已知1
    mixedGC触发条件:
        current heap / total heap > IHOP 
        (current eden + current old) / total heap > IHOP
    mixedGC前总是会触发YoungGC:
        current eden = 0

    综上满足该条件时会触发mixedGC:
        current old / total heap > IHOP

已知2
    年老代 = 年老代占比 * 总堆
        total old = (1 - total eden percent) * total heap


假设期望年老代使用占比达到 A 时触发mixedGC,则
    current old = total old * A

将 已知2 代入 假设 得
    current old = (1 - total eden percent) * total heap * A

再代入 已知1 得
    (1 - total eden percent) * total heap * A / total heap > IHOP

简化得
    IHOP < (1 - total eden percent) * A

total eden percent 取值范围: G1NewSizePercent ~ G1MaxNewSizePercent
为保证能正常mixedGC,total eden percent 取 G1MaxNewSizePercent

最终
    IHOP < (1 - G1MaxNewSizePercent) * A

GC日志

-Xloggc:./tmp/gc-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径

%t 后缀格式生成的时间戳格式为 YYYY-MM-DD_HH-MM-SS。因此,生成的日志文件名看起来像这样 gc-2019-01-29_20-41-47.log

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

推荐阅读更多精彩内容

  • 一、Java内存布局 1、Java内部布局全貌 JVM包含两个子系统和两个组件: 两个子系统为Class load...
    yaco阅读 4,457评论 1 42
  • 本文主要介绍 JVM和GC解析如有需要,可以参考如有帮助,不忘 点赞 ❥创作不易,白嫖无义! 一、OOM的认识 S...
    码农很低调阅读 193评论 0 0
  • JVM内存结构简介(jdk1.8) JVM层的GC调优是生产环境上必不可少的一个环节,因为我们需要确定这个进程可以...
    Lemonrel阅读 315评论 0 0
  • JVM与调优 imooc JVM Markdown JVM的内存结构 运行时数据区 程序计数器PC Regist...
    心释逍遥lx阅读 601评论 0 1
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,543评论 28 53