zoukankan      html  css  js  c++  java
  • JVM垃圾收集 知识汇总

    总结下自己掌握的关于JVM垃圾回收相关的知识点~

    1. 什么样的对象为垃圾?

    引用计数法

    无法解决循环依赖问题

    可达性分析法

    从一些roots对象出发,沿着roots对象追踪对象上的引用,能追踪到的对象都是存活的对象;没有被roots对象引用的视为垃圾对象。
    可以作为GC Roots的对象有:

    • 虚拟机栈中引用的对象
      常见的就是:Object obj = new Object();
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI引用的对象

    2. 垃圾收集算法

    标记-清除

    标记出所有需要回收的对象,标记完成后回收被标记的对象。
    有两个缺点:

    1. 会产生内存碎片,内存碎片太多可能会导致无法分配较大的对象(需要连续的内存空间)
    2. 标记和清除效率不高

    复制

    内存1分为2,每次只使用其中的1半。当其中的一块空间用完了,就将还存活着的对象复制到另外一块内存空间上,然后把已使用的那一块空间一次清理掉
    有2个缺点:

    1. 内存空间浪费,因为每次只使用一半
    2. 存活对象较多时,进行较多的对象复制,效率下降

    标记-整理

    标记过程仍与“标记-清除”算法一样,但是后续不是直接对可回收对象进行清理。而是让存活的对象都向一端移动,最后清楚掉端边界以外的内存。

    3. JVM堆的分代

    根据相关实验(?....?), 很多对象存活时间都是很短的, 少部分的对象存活时间会稍长一些,根据这些特性吧,JVM将堆化分为新生代老年代

    相关参数配置:

    堆的大小: -Xms4G -Xmx4G
    新生代和老年代比例:默认1:2,
    新生代和survior比例默认: 8:1:1

    4. 垃圾回收器的分类

    4.1 工作在Young区的垃圾收集器

    名称 介绍 采用的算法 STW
    Serial 线程收集年轻代 复制算法 STW
    ParNew 多线程收集年轻代 复制算法 STW
    Parallel Scavenge 吞吐量优先的多线程收集年轻代 复制算法 STW

    4.2 工作在Old区的垃圾收集器

    名称 介绍 采用的算法 STW
    Serial Old 单线程收集Old区 标记-整理 STW
    Parallel Old 多线程收集Old区 标记-整理 STW
    CMS 并发标记清除收集Old区 标记-清除 和用户线程并行

    4.3 YoungGC、MajorGC、MixedGC、FullGC

    • YoungGC:发生在年轻代的GC
    • MajorGC:发生在老年代的GC ,只有CMS
    • MixedGC: 发生在年轻代和老年代的GC, 只有G1
    • FullGC指的是:年轻代+老年代+方法区(元空间/永久代) 整个堆上进行垃圾收集
      产生FullGC的原因
      1. 当Eden区分配满的时候,发现之前younggc的平均晋升对象的大小超过当前Old区的剩余空间时,则不会触发Younggc了而会触发FullGC(除CMS外[它只收集old区],其他能收集old区的垃圾收集器都会收集整个堆)。所以不需要额外触发一次YoungGC,如果触发了YoungGC,可以理解为了减轻fullgc的压力吧。
      2. 当方法区无法分配内存的时候,如Perm Generation、Metaspace满了,会触发FullGC
      3. 调用System.gc()会触发FullGC
      4. 执行jmap -dump:live,format=b,file=heap.bin <pid> 的时候触发FullGC

    4.4 CMS-并发标记清除

    一款工作在Old区、且可以和用户线程并发执行的基于标记清除算法的老年代收集器,为了缩短用户线程的暂停时间。

    1. CMS的工作步骤:
    步骤 解释 STW
    初始标记 标记那些roots对象 STW
    并发标记 从roots对象出发标记存活的对象,用户线程不用暂停 非STW
    重新标记 由于步骤2用户线程也在执行,需要重新标记那些引用发生变化的部分 STW
    并发收集 多线程并发收集,和用户线程并发执行 非STW
    2. CMS的优缺点
    • 优点
      可以让垃圾收集工作和用户线程并发执行,减少系统stw卡顿时间,对响应时间要求高的系统比较何时。
    • 缺点
      1.系统吞吐量降低,因为垃圾收集线程占用了cpu的时间
      2.产生内存碎片

      由于CMS是基于“标记-清除”算法实现的收集器,所以会产生内存碎片。 CMS收集器额外又提供了一个可选择的参数-XX:+UseCMSCompactAtFullCollection开关参数(默认开启),用于CMS收集器在顶不住要进行FullGC的时候开启内存碎片的合并整理过程,内存整理的过程是无法并发执行的,所以会导致停顿时间比长。 虚拟机又提供了一个参数-XX:CMSFullGCsBeforeCompaction用于设置执行多少次不压缩的FULL GC后,跟着来一次带压缩的。默认值是0,表示每次进入FULL GC都进行碎片整理

    1. 产生浮动垃圾

      因为并行收集阶段,用户线程也在执行,有可能新产生垃圾对象,这些对象在本次回收阶段无法被回收,只能等下次了
      所以CMS收集器需要预留足够的空间给用户线程使用,不然会出现Concurrent Mode Failure失败。JDK1.6中,CMS收集器启动的阈值是92%,也就是当老年代使用了92%的空间后就会开启CMS收集,不过我们可以通过参数-XX:CMSInitiatingOccupancyFraction=<n>的值来提高触发百分比,可以降低这个值,给用户线程留多一些空间

    2. FullGC会很慢

      当cms清理不及时的时候,应用线程新产生的对象没有足够的内存可分配时,CMS顶不住了会启动Serial Old进行一次FullGC,这个可就慢了啊! (CMS是不进行FullGC的)

    3. CMS的参数

    3.1 -XX:+UseConcMarkSweepGC:启用cms
    3.2 -XX:ConcGCThreads:并发的GC线程数
    3.3 -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理,清理碎片
    3.4 -XX:CMSFullGCsBeforeCompaction=0:多少次FullGC之后压缩一次,默认是0,代表每次 FullGC后都会压缩一次
    3.5 -XX:CMSInitiatingOccupancyFraction=92: 默认值: 92 当老年代使用达到该比例时会触发CMSGC
    3.6 -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(- XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定 值,后续则会自动调整
    3.7 -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少 老年代对年轻代的引用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在 remark阶段
    正式因为上面这些问题,所以没有任意一个jdk版本默认采用CMS收集器,但是它产生的意义是很大的,并发收集!

    4.5 G1

    4.5.1 G1堆的划分

    G1的出现就是来替代CMS的,它能提供更低的延迟。与其他垃圾收集器相比,G1在内存上属于逻辑分代,物理不分代。堆内存不再划分为连续的年轻代和老年代,而是划分为一定数量的堆区域 heap region,这些region可以是Eden,Surviror,Old区的任何一种。所有的Eden区和Surviror区统称为年轻代,所有的Old区称为老年代。 所以是逻辑上分代, 而且某个区域的类型是可变的(当前时刻是属于Eden区,可能等会就是Old区了)。
    G1的堆结构

    有些点需要注意:

    1. 整个堆被划分为很多个(不超过2048)固定大小的Region,大小可以配置从1M(默认)~32M(-XX:G1HeapRegionSize但是不建议手动改)
    2. 所有的Eden、Survivor在逻辑上统称为年轻代,且不需要连续,这样可以使得Resize Region变得容易
    3. 所有的Old Region在逻辑上统称为老年代,且不需要连续
    4. 还有一种Region类型是:Humongous regions,它用来存放超过Region 50%大小的对象,他们被存储到专门的连续的Region中。而不是把他们存到Old的Region,因为可以节约Old区的空间,降低发生gc的频率。
    5. 一个Region在当时是属于Eden,可能过会儿就成为了Old,也可能是Survior
    4.5.2 G1的YoungGC

    注意到上图的Young区是不需要连续的, 存活的对象将被转移(复制或者移动)到Survivor区或者Old区(age达到了阈值)。
    总结YoungGC的收集过程:

    1. YoungGC只收集Young generation里面的regions
    2. YoungGC是需要STW的,younggc使用多线程完成。
    3. Eden区和Surviror区的大小会被计算用于下一次YoungGC,用于优化给定的gc暂停时间,这种方式使得resize region变得容易,可以在需要的时候把他们变大,或者变小。

    YoungGC并不是说现有的Eden区放满了就会马上触发,而且G1会计算下现在Eden区回收大 概要多久时间,如果回收时间远远小于参数 -XX:MaxGCPauseMills 设定的值,那么增加年轻代 的region,继续给新对象存放,不会马上做Young GC,直到下一次Eden区放满,G1计算回收时 间接近参数 -XX:MaxGCPauseMills 设定的值,那么就会触发Young GC

    4.5.3 G1的MixedGC

    在old区回收会经历下面几个阶段,一些阶段也会在Young区发生。

    阶段 描述
    (1) Initial Mark
    (Stop the World Event)
    会STW. 依托于一次普通的YoungGC. 在Old区标记出roots对象.
    逻辑上global concurrent marking与evacuation是相对独立的。但是global concurrent marking之中initial marking要做的事情正好跟young GC要做的事情有重叠——遍历根集合,所以在实现上把它们安排在一起做。
    一个young GC可以顺带做initial marking,也可以不做;
    一个正常的initial marking(除非是System.gc()+ -XX:+ExplicitGCInvokesConcurrent)总是搭在young GC上做。
    (2) Root Region Scanning Scan survivor regions for references into the old generation. This happens while the application continues to run. The phase must be completed before a young GC can occur.
    (3) Concurrent Marking 在整个堆上查找存活的对象,计算存活率较低的regions. 此时用户线程可以运行. 这个阶段可以被年轻代垃圾收集中断.
    (4) Remark
    (Stop the World Event)
    重新标记存活的对象. 用一种叫做 snapshot-at-the-beginning (SATB) 的算法(比CMS的算法快).
    (5) Cleanup
    (Stop the World Event and Concurrent)
    1. 对存活的对象和空regions做计算. (Stop the world)
    2. Scrubs the Remembered Sets. (Stop the world)
    3. 回收空的Regions. (Concurrent)
    (*) Copying
    (Stop the World Event)
    移动或者复制存活的对象到未使用的regions里面,此阶段是STW的.
    可以在年轻代完成(在log中标记为:[GC pause (young)]).
    也可以在年轻代和老年代完成(在log中标记为: [GC Pause (mixed)]).

    MixedGC: 收集young gen里的所有region,外加若干选定的old gen region和大对象Region。控制mixed GC开销的手段是选多少个、哪几个old gen region, 根据用户设置的期望暂停时间,来选择那些收益较高的old regions。 所以:MixedGC不是FullGC。

    4.5.4 G1的FullGC

    如果MixedGC的速度跟不上程序程序分配新对象的速度,导致Old区被填满
    会切换到G1之外的Serial Old单线程GC来收集整个堆FullGC.

    4.5.5 G1最佳实践配置
    1. 开启G1

      java -Xmx4G -Xms4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45

    2. 完整的G1的配置:
    Option and Default Value Description
    -XX:+UseG1GC Use the Garbage First (G1) Collector
    -XX:MaxGCPauseMillis=n Sets a target for the maximum GC pause time. This is a soft goal, and the JVM will make its best effort to achieve it.
    -XX:InitiatingHeapOccupancyPercent=n Percentage of the (entire) heap occupancy to start a concurrent GC cycle. It is used by GCs that trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations (e.g., G1). A value of 0 denotes 'do constant GC cycles'. The default value is 45.
    -XX:NewRatio=n Ratio of new/old generation sizes. The default value is 2.
    -XX:SurvivorRatio=n Ratio of eden/survivor space size. The default value is 8.
    -XX:MaxTenuringThreshold=n Maximum value for tenuring threshold. The default value is 15.
    -XX:ParallelGCThreads=n Sets the number of threads used during parallel phases of the garbage collectors. The default value varies with the platform on which the JVM is running.
    -XX:ConcGCThreads=n Number of threads concurrent garbage collectors will use. The default value varies with the platform on which the JVM is running.
    -XX:G1ReservePercent=n Sets the amount of heap that is reserved as a false ceiling to reduce the possibility of promotion failure. The default value is 10.
    -XX:G1HeapRegionSize=n With G1 the Java heap is subdivided into uniformly sized regions. This sets the size of the individual sub-divisions. The default value of this parameter is determined ergonomically based upon heap size. The minimum value is 1Mb and the maximum value is 32Mb.

    5. 记录GC的日志

    -XX:+PrintGCDetails
    -XX:+PrintGCTimeStamps
    -XX:+PrintGCDateStamps
    -XX:+UseGCLogFileRotation
    -XX:NumberOfGCLogFiles=10
    -XX:GCLogFileSize=10m
    -Xloggc:/xxx/logs/gc.log

    持续更新~~

    参考文献:

    Major GC和Full GC的区别是什么?
    Oracle G1
    R大神关于G1的原理介绍
    美团-G1介绍

  • 相关阅读:
    JS调试debug
    避免使用 JS 特性 with(obj){}
    bit Byte KB MB GB TB 单位换算
    C语言中连接器介绍
    [bzoj3600]没有人的算术
    [bzoj4373]算术天才⑨与等差数列
    [bzoj4151][AMPPZ2014]The Cave
    [bzoj4906][BeiJing2017]喷式水战改
    [bzoj4908][BeiJing2017]开车
    [Codeforces Round#417 Div.2]
  • 原文地址:https://www.cnblogs.com/yangweiqiang/p/13525862.html
Copyright © 2011-2022 走看看