ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

GC基础知识

2022-01-12 23:34:13  阅读:140  来源: 互联网

标签:对象 回收 基础知识 XX GC CMS 分配


1.什么是垃圾

没有任何引用指向的一个对象或者多个对象(循环引用)

2.如何定位垃圾

  1. 引用计数(ReferenceCount)

    就是对每个对象都追踪指向它们的引用数,如果引用数为0,就说明这个对象是内存垃圾了。但是这个方法存在缺陷,如果多个对象之间存在循环引用,但是这些对象没有被外部引用,这些对象实质上也是垃圾,但是没有办法被标记

  2. 根可达算法(RootSearching),jvm采用的是这种

    指从根对象开始扫描,所有可以被扫到的对象都被标记为存活对象,而扫不到的对象就是垃圾对象。对扫到的对象一般是在该对象的header中打上一个标识,表示当前对象还是存活对象。

    那么什么是根对象呢?主要由下面四种对象构成:

    • main方法的栈帧中的对象
    • class文件中的静态变量
    • 常量池中的对象
    • JNI用到的对象

3.常见的垃圾回收算法

  1. 标记清除(mark sweep)

    image

    • 执行流程:标记阶段,从根对象开始进行遍历,对根对象可以访问到的对象都打上一个标识,一般在对象头中,将其记录为可达对象;在清除阶段对堆内存从头到尾进行遍历,如果发现某个对象没有标记为可达对象(通过读取对象头信息)则就将其回收
    • 优点:算法简单,存活对象比较多的时候效率较高,适合old区
    • 缺点:位置不连续容易产生碎片,两遍扫描效率偏低
  2. 拷贝算法 (copying)

    image

    • 执行流程:从根对象开始进行遍历,对根对象可以访问到对象移动到一个专门的内存区域(java中叫s0、s1),然后回收这块内存区域之外的其他内存区域
    • 优点:没有碎片,只扫描一次效率提高,适合存活对象较少的情况,比如年轻代
    • 缺点:浪费空间,移动复制对象需要调整对象引用
  3. 标记-整理(mark compact)

    image

    • 执行流程:标记阶段,从根对象开始进行遍历,对根对象可以访问到的对象都打上一个标识,一般在对象头中,将其记录为可达对象;整理阶段,遍历整个堆,将存活对象全部向一端移动,然后直接清理掉边界以外的内存

    • 优点:不会产生碎片方便对象分配,不会存在内存减半问题,适合old区

    • 缺点:扫描2次,需要移动对象,效率偏低

4.JVM内存分代模型

  1. 内存分代模型

image

  1. GC概念

image

  • MinorGC/YGC:年轻代空间耗尽时触发

  • MajorGC/FGC:在老年代无法继续分配空间时触发,新生代老年代同时进行回收

  1. 部分垃圾回收器使用的模型

    除Epsilon、ZGC、Shenandoah之外的GC都是使用逻辑分代模型

    G1是逻辑分代,物理不分代

    除此之外其他垃圾回收器不仅逻辑分代,而且物理分代

  2. 新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace

    永久代 元数据 - Class

    永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(受限于物理内存)

    字符串常量 1.7 - 永久代,1.8 - 堆

    MethodArea逻辑概念 - 永久代、元数据具体实现

    https://blog.csdn.net/qq_27062249/article/details/116952519

  3. 新生代 = Eden + 2个suvivor区

    YGC回收之后,大多数的对象会被回收,活着的进入s0

    再次YGC,活着的对象eden + s0 -> s1

    再次YGC,eden + s1 -> s0

    年龄足够 -> 老年代 (年龄限制XX:MaxTenuringThreshold :其他垃圾回收器15,CMS 6)

    s区装不下 -> 老年代

  4. 老年代

    顽固分子

    老年代满了FGC Full GC

  5. GC Tuning (Generation)

    尽量减少FGC

    MinorGC = YGC

    MajorGC = FGC

  6. 栈上分配

    1. 什么是栈上分配:对象在栈上分配而不是堆
    2. 栈生分配优点:不需要GC介入去回收这个对象,出栈即释放资源,可以提高性能
    3. 哪些对象可以在栈上分配:线程私有小对象、无逃逸、支持标量替换
    4. 如何开启栈上分配:jvm默认支持,可以通过 -XX:-DoEscapeAnalysis关闭逃逸分析, -XX:-EliminateAllocations关闭标量替换,从而关闭栈上分配,一般情况下无需调整
  7. 线程本地分配TLAB(Thread Local Allocation Buffer)

    1. 什么是本地分配:JVM为每一个线程在初始化时先单独分配一块Buffer,该空间是线程私有的,当线程执行时需要为对象分配内存时,就是用自己的Buffer空间分配,这样就解决了多线程因共用同一JVM堆空间而产生的同步操作冲突问题。TLAB使用的堆内存空间其实还是eden区,默认1%
    2. 本地分配优点:多线程的时候不用竞争eden就可以申请内存,提高效率
    3. 如何开启本地分配:默认开启,可以通过-XX:-UseTLAB来关闭TLAB,一般情况下无需调整
  8. 动态年龄

    1. 年龄从小到大进行累加,当加入某个年龄段后,累加和超过survivor区域*TargetSurvivorRatio的时候,就从这个年龄段往上的年龄的对象进行晋升。比如年龄1的占用了33%,年龄2的占用了33%,累加和超过默认的TargetSurvivorRatio(50%),年龄2和年龄3的对象都要晋升。

    2. -XX:TargetSurvivorRatio这个参数来控制,默认为50%

    3. https://www.jianshu.com/p/989d3b06a49d

  9. 分配担保

    1. YGC期间 survivor区空间不够了,空间担保直接进入老年代(当在新生代无法分配内存的时候,把新生代的对象转移到老生代,然后把新对象放入腾空的新生代。)
    2. https://cloud.tencent.com/developer/article/1082730
  10. 对象何时进入老年代

    1. 超过-XX:MaxTenuringThreshold指定次数(YGC),Parallel Scavenge 15,CMS 6,G1 15

    2. 动态年龄 s1->s2超过50%

  11. 对象分配过程图

image

new一个对象,先进行线程栈上分配判断,如果能进行则进入栈上分配,然后结束

如果无法进行栈上分配则判断是否是大对象,如果是大对象则直接进入老年代分配,然后结束

如果不是大对象则进行TLAB判断,不过是否TLAB都是在eden区进行分配

然后执行gc,判断对象是否存活,没有存活则结束;如果对象存活则进入s1;再次执行gc再次判断对象是否存活,如果存活判断年龄是否大于指定参数,如果大于指定配置则进入老年代(cms 6,其他 15);如果年龄小于指定参数则进入s2;然后反复此步骤直接到结束

5. 垃圾回收器类型

image

红色虚线表示常见组合

  1. JDK诞生 Serial追随;为了提高效率诞生了PS;为了配合CMS诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前任何一个JDK版本默认是CMS;并发垃圾回收是因为无法忍受STW

  2. Serial,用于年轻代,会有STW,采用复制算法,使用单线程进行GC也就是串行回收(黄色箭头表示GC)

image

  1. Parallel Scavenge(PS),用于年轻代,会有STW,采用复制算法,使用多线程进行GC也就是并行回收(黄色箭头表示GC)

    image

  2. ParNew ,用于年轻代,会有STW,采用复制算法,使用多线程进行GC也就是并行回收(黄色箭头表示GC),与PS不同的是有增强功能配合CMS,例如它可以在CMS的并发阶段运行ParNew执行所需的同步

image

  1. SerialOld ,用于老年代,会有STW,采用标记-清除-压缩(Mark-Sweep-Compat,多次GC后才压缩是标记清除和标记压缩的结合。但不算在GC四大算法中),使用单线程进行GC也就是串行回收(黄色箭头表示GC)

image

  1. ParallelOld,用于老年代,会有STW,采用标记整理算法,使用多线程进行GC也就是并行回收(黄色箭头表示GC)

image

  1. ConcurrentMarkSwee,用于老年代,并发执行,垃圾回收和应用程序同时运行,降低STW的时间(200ms)。CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定。CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收。算法采用三色标记 + Incremental Update

  2. G1,设计目标在10ms内,算法采用三色标记 + SATB

  3. ZGC ,设计目标在1ms内,算法采用ColoredPointers + LoadBarrier

  4. Shenandoah,算法ColoredPointers + WriteBarrie

  5. Eplison,debug用的,不会做什么事的GC

  6. PS 和 PN区别:https://docs.oracle.com/en/java/javase/13/gctuning/ergonomics.html

  7. 垃圾收集器跟内存大小的关系:

    Serial 几十兆

    PS 上百兆 - 几个G

    CMS - 20G

    G1 - 上百G

    ZGC - 4T - 16T(JDK13)

  8. 1.8默认的垃圾回收:PS + ParallelOld

5. 常见垃圾回收器组合参数设定:(1.8)

  • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old

    适用于小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器

  • -XX:+UseParNewGC = ParNew + SerialOld

    这个组合已经很少用(在某些版本中已经废弃)

    https://stackoverflow.com/questions/34962257/why-remove-support-for-parnewserialold-anddefnewcms-in-the-future

  • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old

  • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】

  • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old

  • -XX:+UseG1GC = G1

  • Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC

    java +XX:+PrintCommandLineFlags -version

    通过GC的日志来分辨

  • Linux下1.8版本默认的垃圾回收器到底是什么?

    1.8.0_181 默认(看不出来)Copy MarkCompact

    1.8.0_222 默认 PS + PO

标签:对象,回收,基础知识,XX,GC,CMS,分配
来源: https://www.cnblogs.com/ZT-666/p/15734688.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有