• JVM学习07——GC垃圾回收


    一、什么是垃圾 garbage?

    没有引用 指向的 对象 都是垃圾

    1. 垃圾回收 Java VS C++

    JavaC++
    GC处理垃圾手工处理垃圾
    开发效率高,执行效率高开发效率低,执行效率高
    缺陷1 :忘记回收会造成内存泄漏
    缺陷2 :回收多次可能造成非法访问

    二、如何找到垃圾?

    2.1 Reference Count 引用计数(如Python)

    一个对象被几个指针指向。就记录其指针的数量
    当 引用==null 时,count - -
    当 count == 0 时,这就是垃圾了

    弊端:不能找到循环引用组成的垃圾
    在这里插入图片描述

    2.2 Root Searching 根可达算法

    GC roots : 线程栈变量、 静态变量、常量池、JNI指针( java本地接口 )
    以上 根 指向的地方、可以根据引用抵达的地方 不是垃圾,其余都是

    在这里插入图片描述

    三、GC常用的垃圾清除算法

    3.1 Mark-Sweep 标记清除法 ( 标记的活 )

    存活对象多时效率高,如老年代

    标记还 有用的对象,清除无用的
    存活对象比较多的情况下,效率较高
    所以不适合伊甸园区
    
    算法缺陷
    	两遍扫描,效率偏低(第一遍标记、第二遍清除)
    	容易产生碎片
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2 Copying 拷贝法

    存活对象少时效率高,适用于伊甸园区内存一分为2
    找到有用的,把有用的对象拷贝到另一边空地,全找完后本区域全部清除

    优点弊端
    适用于存活对象较少的情况(如伊甸园区),只扫描一次,效率高移动复制对象,需要调整对象的引用(所以 java 即使一直指向同一个对象,它的引用的值也会变。)
    没有碎片空间浪费

    3.3 Mark-Compact 标记压缩法

    第一遍先标记出还有用的对象
    第二遍把有用的对象挪到一起,清除剩下的垃圾。所以剩下的空间就很整齐了。

    优点弊端
    不会产生碎片,方便对象分配扫描两次
    不会产生内存减半需要移动对象,效率偏低

    四、堆内存逻辑分区

    分代算法
    部分垃圾回收期使用的模型
    除 Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型
    G1 是逻辑 分带,物理不分代
    除此之外 都 逻辑分代 + 物理分代

    在这里插入图片描述

    4.1 新生代 new / young

    1. 堆内存占比 1/3
    2. 存活对象少
    3. Copying算法
    4. MinorGC/YGC :年轻代空间耗尽时触发(-Xmn)

    新生代分为 伊甸园区 eden 和 幸存者1区 servivor 1 和 幸存者2区 servivior 2
    比例 8 :1 :1

    而新生代和老年代比例 1 :2

    4.2 老年代 old / tenured(终身)

    1. 堆内存占比 2/3
    2. 存活对象多
    3. Mark Compact算法或MarkSweep算法;
    4. MajorGC/FullGC:在老年代无法继续分配空间时触发,新生代和老年代同时进行回收(-Xms -Xmn)

    注意:永久代Permanant 是方法区MethodArea

    新生代和老年代在堆,永久代在方法区
    存class信息、代码的编译信息等

    方法区的实现

    JDK1.8之前JDK1.8之后
    永久代元数据区
    必须指定大小元数据区可以设置大小,也可以不设置,无上限(受限于物理内存)
    字符串常量在永久代字符串常量在堆

    五、对象分配

    5.1 哪些对象会 栈上分配?

    1. 线程私有小对象
    2. 无逃逸 (这个对象就某一块代码里集中使用,其余地方不用)
    3. 支持标量替换 (用几个普通的类型就能代替这个对象)
    4. 无需调整

    -XX:-DoEscapeAnalysis 去掉逃逸分析
    -XX:-DoEscapeAnalysis 去掉标量替换
    -XX:-UseTLAB 去掉使用线程本地分配

    5.2 哪些对象会 线程本地分配?

    线程本地分配 TLAB ( Thread Local Allocation Buffer )
    占用eden,默认1%
    每个线程在伊甸园区取1%作为自己的,每次分配内存时,首先往自己的线程本地分配,
    多线程时候不用竞争eden就可以申请空间,提高效率

    1. 小对象
    2. 无需调整

    六、对象何时进入老年代?

    1. 大对象 直接进入老年代
    2. 幸存者区达到最大年龄的对象 ( 默认15.因为在markword中年龄占4个bit,即最大为1111 )
      超过 XX:MaxTenuringThreshold指定次数 YGC
      Parallel Scavenge 15
      CMS 6
      G1 15
    3. 动态年龄
      幸存者1区 或 幸存者2区 的对象超过当前区总内存的50%时
      把年龄最大的放入老年代(也就是说不一定到15岁)
    4. 分配担保
      YGC(新生代垃圾回收)期间,survivor区空间不够了,通过空间担保直接进入老年代

    七、常见的垃圾回收器

    老年代内存不够用了,触发FGC,清理老年代,可以用以下垃圾回收器回收;
    除了圈出来的是 物理不分代 + 逻辑分代
    其余都是 物理分代 + 逻辑分代

    在这里插入图片描述

    JDK诞生,Serial追随,为了提高效率,诞生了Parallel Scavenge,
    为了配合CMS,诞生了PaarNew.
    CMS是JDK 1.4 之后引入的
    CMS是里程碑式的GC, 开启了并发回收
    但是CMS毛病多,因此目前没有任何一个JDK版本默认是CMS,都是默认PS
    CMS 收集器是以 获取 最短停顿时间 为 目标的收集器
    并行收集

    7.1 Serial + Serial Old (单线程)

    STW 垃圾清理时工作暂停,单线程清理垃圾
    目前还没有不会产生STW 的垃圾回收器
    STW Stop The World

    7.2 Parallel(平行) Scavenge(回收) + Parallel Old

    当前JVM默认是这种 ( 并行的 )
    PS + PO 和STW差不多,垃圾清理时工作暂停,不过是多线程清理垃圾。吞吐量优先

    7.3 ParNew 多线程 + CMS

    默认线程数为CPU核数,响应时间优先
    是PS的变种,和CMS配合使用

    7.4 G1(10ms)

    JDK1.8出现,1.9完善

    7.5 ZGC (1ms)

    7.6 Shenandoah

    7.7 Epsilon

    debug用的

    7.8 CMS (垃圾回收和工作线程并发执行, 1.4+)

    Concurrent Mark Sweep 并发标记清除
    [多个线程同时回收是并行,这里是并发,可以垃圾回收和工作线程并发执行]

    并发垃圾回收是因为无法忍受STW
    以前内存不大,停下来清理也快。
    现在内存很大,停下来清理要很久很久,甚至要停几天

    CMS也是老年代垃圾回收器,老年代满了就触发 FGC

    7.8.1 CMS过程 ( 高响应,低停顿 )

    在这里插入图片描述

    1. 初始标记阶段 initial mark
      --------- STW 标记存活的对象,只标记一些根对象 (根可达法)
      --------- 所以速度 很快
      在这里插入图片描述
      ------------- 本阶段仅标记 GC roots 直接连接的对象
    1. 并发标记阶段 concurrent mark
      --------- 边 标记 边 运行 工作线程
    2. 重新标记阶段 remark (大多数的垃圾在并发标记时期已经标记完了)
      --------- STW 过程,工作线程停止,标记新产生的垃圾
      --------- 因为新产生的垃圾并不多,所以也很快
    3. 并发清理阶段 concurrent sweep
      --------- 边清理标记的垃圾,边运行工作线程,此时产生的浮动垃圾由CMS下次清理

    7.8.2 CMS的两大问题

    1. 浮动垃圾

    解决方案:降低触发CMS的阈值,保持老年代有足够的空间

    Concurrent Model Failure
    PromotionFaoled
    如果是有很多碎片了,实在放不下对象了
    CMS就会把老奶奶 Old Sorial 请出来清理
    会SWT,停止工作线程,Old Sorial要清理很久很久很久
    降低CMS触发的阈值

    1. 内存碎片化

    CMS
    Concurrent Mark Sweep
    并发标记清除法
    标记清除,会产生大量碎片

    -XX:CNSInitiationOccupancyFraction 92%
    降低触发CMS的阈值,保持老年代有足够的空间

    八、并发标记的算法 Concurrent Mark 阶段的算法

    CMS ( 三色标记法 + incremental Update )
    G1 ( 三色标记法 + SATB )
    ZGC ( Coloredpointers 颜色指针 + 写屏障 )
    Shenandoah ( ColoredPointers + 读屏障 )

    8.1 三色扫描算法:白 灰 黑

    在这里插入图片描述

    在并发标记时,引用 可能 产生 变化,白色对象有可能被错误回收

    1. 白:没有遍历到的节点
    2. 灰:自己标记完成,还没来得及标记fields
    3. 黑:自己已经标记,fields都标记完成

    解决方案

    1. SATB
      shapshot at the begining 在起始的时候做一个快照
      当B->D消失时,要把和这个引用 推到 GC的堆栈,保证D还能被GC扫描到
    2. incurmental Update
      ( 自己的白的没扫完,变成黑的重新扫 )
      当一个白色对象被一个黑色对象引用,将黑色对象重新标记为灰色,让collector重新扫描
  • 相关阅读:
    AI智能识别如何助力PDF,轻松实现文档处理?
    基于单片机设计的智能风扇(红外线无线控制开关调速定时)
    盘点66个Pandas函数,轻松实现“数据清洗”
    java计算机毕业设计流浪狗领养系统源码+mysql数据库+系统+lw文档+部署
    网络故障排查思路二
    基金客户服务
    工厂模式之工厂方法模式(常用)
    计算机毕业论文微信小程序毕业设计SSM校园论坛+后台管理系统|前后分离VUE[包运行成功]
    云帆在线学习考试系统介绍
    召回率与精确率的理解
  • 原文地址:https://blog.csdn.net/niTaoTaoa/article/details/126301254