JVM学习笔记22 垃圾回收理论知识

bilibili-JVM学习笔记22 垃圾回收理论知识
The Java Virtual Machine Specification - Java SE 8 Edition

JVM学习笔记18 字节码知识总结
JVM学习笔记19 JVM内存空间
JVM学习笔记20 jvisualvm
JVM学习笔记21 java 工具

基于 java 1.8.0

JVM垃圾回收重要理论剖析 74

  1. 运行时区域

在这里插入图片描述

  • 程序计数器 PC
  • 本地方法栈
  • Java虚拟机栈 (JVM Stack)
    • Java 虚拟机栈描述的是 Java 方法的执行模型:
    • 每个方法执行的时候都会创建一个栈帧(Frame)用于存放局部变量表、操作数栈、动态链接、方法出口地址等信息。
    • 一个方法的执行过程,就是这个方法对于栈帧的入栈和出栈过程;
    • 线程隔离
    • 存放对象的实例数据
    • JVM 管理内存中最大的一块
    • GC 主要的工作区域,为了更高效的 GC,会把堆细分更多的子区域;
    • 线程共享
  • 方法区域
    • 存放每个 Class 的结构信息,包括:常量池、字段描述、方法描述
    • GC 的非主要工作区域

案例分析:

public void method1(){
    Object obj = new Object();
}
  • 生成了两部分的内存区域:
    • 1、 obj 这个引用变量,因为是方法内的变量,放到虚拟机栈区域;
    • 2、 真正 Object class 的实例对象,放到堆空间区域;
  • 上述的 new 语句一共需要消耗 12 byte 内存空间:
    • JVM 规定引用占 4 byte (在 JVM Stack 中),而空对象需要 8 byte (在 Heap)
  • 方法结束后,对应方法的 Stack 中的变量马上回收,但是 Heap 重点中的对象要等到 GC 来回收

  1. 垃圾判断算法
  • 引用计数算法(Reference Counting)
    • 给对象添加一个引用计数器,当有一个地方引用它时,计数器加1;当引用失效时,计数器减1;计数器为 0 的对象就是不可能再被使用的;
    • 算法简单,效率高效;
    • 存在对象循环引用的问题;
    • 现代垃圾收集器基本不采用;
  • 根搜索算法(Root Tracing)
    • 在实际的生产语言中(Java、C#等),都是使用根搜索算法判定对象是否存活
    • 算法基本思路就是通过一系列的称为 “GC Roots” 的点作为起始点进行向下搜索,当一个对象到 GC Roots 没有任何引用链(Reference Chain)相连,则证明此对象不可达,即是不可用对象;
    • java 语言中的 GC Roots 包括:
      • 在虚拟机栈(栈帧中的本地变量)中的引用
      • 方法区中的静态引用
      • JNI(即一般说的 Native 方法) 中的引用

方法区

  • Java 虚拟机规范表示不要求虚拟机实现在方法区中实现 GC ,因为在方法区中进行 GC 性价比一般比较低;
  • 当前的商业 JVM 都有实现方法区的 GC ,主要回收两部分内容:
    • 废弃常量
    • 无用类(满足以下三个要求):
      • 该类的所有实例都已经被 GC ,也就是 JVM 中不存在该 Class 的任何实例;
      • 加载该类的 ClassLoader 已经被 GC ;
      • 该类对应的 java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问此类的方法;
  • 在大量使用反射、动态代理、CGLib 等字节码框架、动态生成 JSP 以及 OSGi 这类频繁自定义 ClassLoader 的场景都需要 JVM 具备类卸载的支持以保证方法区不会发生内存溢出

JVM垃圾回收算法分析与演示 75

  1. 垃圾回收算法
  • 标记-清除算法(Mark-Sweep)
    • 算法分为标记清除两个阶段,首先标记出所有需要回收的对象,然后回收所有需要回收的对象;
    • 缺点
      • 效率问题:标记和清理两个过程的效率都不高;
        • 需要扫描所有对象,堆越大,GC 越慢;
      • 空间问题:标记清理之后会产生大量不连续的内存碎片,空间碎片太多可能会导致后续使用中无法找到足够大的连续内存而提前触发另一次的垃圾搜集动作;
        • GC 次数越多,碎片越严重;
  • 标记-整理算法(Mark-Compact)
    • 标记过程和上述一样,但后续步骤不是进行直接清理,而是令所有存活的对象一端移动,然后直接清理掉这端边界以外的内存;
    • 没有内存碎片;
    • 比 Mark-Sweep 耗费更多的时间进行 Compact(整理);
  • 复制算法(Copying)
    • 将可用内存划分为两块,每次只使用其中的一块,当其中一半的内存用完时,将这一区域中存活的对象复制到另一半内存区域中去,然后就把此区域内存空间一次性清理掉;
    • 每次回收都是对整个半区进行回收,内存分配时也就不用考虑内存碎片的情况了,实现简单,运行高效;
    • 内存空间浪费极其严重,只能使用内存空间的一半。
    • 现代的商业虚拟机中都是使用这一收集算法来回收新生代区域
    • 复制收集算法在对象存活率高的时候,效率有所下降
    • 如果不想浪费一半的空间,就需要有额外的空间进行分配担保用于应付另一半区域内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用复制算法;
    • 只需要扫描存活的对象,效率更高;
    • 不会产生碎片
    • 需要浪费额外的内存作为复制区
    • 复制算法非常适合生命周期比较短的对象,因为每次 GC 总能回收大部分的对象,复制的开销比较小;
    • 根据 IBM 的专门研究,98% 的 Java 对象只会存活 1 个 GC 周期,对这些对象很适合使用复制算法。而且不用 1 : 1 的划分工作区和复制区的空间;
  • 分代收集算法(Generational Collecting)
    • 当前商业虚拟机的垃圾收集都是采用分代收集算法,根据对象不同的存活周期将内存划分为几块;
    • 一般是把 Java 堆分作新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法
    • 譬如新生代每次 GC 都有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

标记-清除算法

在这里插入图片描述

–>

在这里插入图片描述

–>

在这里插入图片描述


分代复制算法:分代算法和复制算法 结合使用

在这里插入图片描述


  1. HotSpot VM
  • HotSpot JVM 6 中共划分为三个代:
    • 年轻代(Young Generation)
      • 新生成的对象一般都放在新生代;(理论上,年轻代对象的生命周期非常短,所以使用复制算法)
      • 分为三个子区域:一个 Eden 区,两个 Survivor 区(可以通过参数设置 Survivor 个数);
      • Eden区 和 两个 Survivor 区的缺省比例是 8 : 1 : 1 ;
        • 可以根据 GC log 的信息调整此比例的大小;
    • 老年代(Old Generation)
      • 存放经过多次 GC 还存活的对象
      • 一般采用 Mark-Sweep 或者 Mark-Compact 算法进行 GC
      • 有多种垃圾收集器可以选择,可以根据具体应用的需求选用合适的垃圾收集器;(吞吐量,响应时间)
    • 永久代(Permanent Generation)
      • 并不属于堆空间,但是 GC 也会涉及到这个区域;
      • 存放了每个 Class 的结构信息,包括:常量池、字段描述、方法描述;

JVM垃圾回收器理论分析与讲解 76

内存结构

在这里插入图片描述


内存分配

  • 堆上分配
    • 大多数情况在 eden 区分配,偶尔直接在 old 上分配;
    • 细节取决于 GC 的实现;
  • 栈上分配
    • 原子类型的局部变量
      • byte boolean char short int float long double

内存回收

  • GC 要做的是将那些 dead 的对象所占用的内存回收掉
    • HotSpot 认为没有引用的对象是 dead 的;
    • HotSpot 将引用分为四种:StrongSoftWeakPhantom
      • Strong Reference 强引用:默认通过 new 对象进行赋值的引用都是强引用;
      • Soft(软引用)、Weak(弱引用)、Phantom(虚引用) 都继承 Reference 类
    • 在 Full GC 时会对 Reference 类型的引用进行特殊处理;
      • Soft(软引用):
        • 内存不够时会被 GC
        • 长期不用也会被 GC
      • Weak(弱引用)
        • 无论内存够不够用,一定会被 GC
        • 当被 mark 为 dead ,会在 ReferenceQueue 中通知
      • Phantom(虚引用)
        • 本来就没引用,当从 jvm heap 中释放时会通知

GC 的时机

  • 在分代模型的基础上,GC 从时机上分为两种:
    • Scavenge GC (又称 Minor GC)
      • 触发时机:
        • 新对象生成时
        • Eden 区空间满了
      • 理论上 Eden 区大多数对象会在 Minor GC 回收掉,复制算法的执行效率很高,Minor GC 时间比较短;
    • Full GC
      • 对整个 JVM 进行整理,包括 Young、Old、Perm(jdk8移除)
      • 主要触发时机:
        • Old 区满了
        • Perm 区满了
        • 手动执行 System.gc()
      • 效率很低,尽量减少 Full GC 的次数;(STW)
  1. 垃圾回收器 Garbage Collector
  • 分代模型 :GC 的宏观愿景
  • 垃圾回收器 : GC 的具体实现
  • HotSpot VM 提供了多种垃圾回收器,我们需要根据具体应用场景采用不同的垃圾回收器;
  • 每种垃圾回收器都有自己的使用场景
  • 并行(Parallel)
    • 指多个收集器的线程同时工作,但是用户线程处于等待状态;
  • 并发(Concurrent)
    • 指收集器在工作的同时,可以允许用户线程工作;
    • 不代表解决了 GC 停顿的问题,在关键的步骤还是要停顿的;如在标记阶段必须停顿,而清除阶段用户线程和GC线程并发执行;

在这里插入图片描述

  • Serial 垃圾收集器
    • 新生代和老年代均可使用
    • 在新生代使用复制收集算法,在老年代采用 Mark-Compact 算法
    • 最早的收集器,单线程收集器,GC 时会暂停所有工作线程(Stop The World STW),没有多线程切换的开销,简单实用
    • HotSpot 虚拟机在 Client 模式下的默认新生代收集器
  • ParNew 垃圾收集器
    • Serial 的多线程版本,除了多个收集线程外,其余行为包括收集算法,STW,对象分配规则,回收策略等都与 Serial 收集器一样;
    • 使用复制算法(因为主要针对新生代)
    • HotSpot 虚拟机在 Server 模式下的默认新生代收集器
    • 只有在多 CPU 的环境下,效率才会比 Serial 收集器高,在单 CPU 的环境下,并不会比 Serial 收集器有更好的效果;
    • 可以通过 -XX:ParallelGCThreads=3 来控制 GC 线程数的多少,需要结合具体 CPU 的个数
  • Parallel Scavenge 垃圾收集器
    • 多线程收集器
    • 使用复制算法
    • 对象分配规则,回收策略都与 ParNew 有所不同;
    • 以吞吐量最大化为目标的收集器实现(即 GC 时间占总运行时间最小),它允许较长时间的 STW 换取总吞吐量最大化;
  • CMS 垃圾收集器 (Concurrent Mark Sweep)
    • 以最短停顿时间为目标的收集器(舍弃吞吐量)
    • 追求最短停顿时间,非常适合 Web 应用
    • 采用标记-清除算法
    • 老年代收集器,一般结合 ParNew 使用
    • Concurrent ,GC 线程和用户线程并发工作
    • 只有在多 CPU 环境下才有意义;
    • 使用 -XX:+UseConcMarkSweepGC 打开
    • 缺点
      • CMS 已牺牲 CPU 资源的代价来减少用户线程的停顿,当 CPU 个数少于 4 的时候,有可能对吞吐量影响很大;
      • CMS 在并发清理的过程中,用户线程还在工作,需要预留一部分空间给用户线程
      • 因为用 Mark-Sweep 算法,会带来碎片化问题,碎片化过多时会容易频繁触发 Full GC
  • Serial Old 垃圾收集器
    • 老年代收集器
    • 单线程收集器
    • 采用 Mark-Compact 算法
  • Parallel Old 垃圾收集器
    • 老年代版本吞吐量优先的收集器
    • 多线程收集器
    • 采用 Mark-Compact 算法
    • JVM 1.6 提供,在此之前,如果新生代使用 Parallel Scavenge 收集器,老年代除 Serial Old 外别无选择;(因为 Parallel Scavenge 无法与 CMS 配合使用)
    • Parallel Scavenge + Parallel Old = 高吞吐量,但 GC 停顿时间可能不理想

Serial 垃圾收集器

在这里插入图片描述

Parallel Old 垃圾收集器

在这里插入图片描述

关于 GC 的 JVM 参数定义

Java HotSpot 7 VM Options
Java Platform, Standard Edition Tools Reference 8 – Solaris 平台

参数 描述
-XX:+UseSerialGC VM 运行在 Client 下的默认值,打开开关后,使用 Serial + Serial Old 的收集器组合进行内存回收
-XX:+UseParNewGC 打开开关后,使用 ParNew + Serial Old 的收集器组合进行内存回收 (??)
-XX:+UseConcMarkSweepGC 打开开关后,使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收,Serial Old 作为 CMS 出现 Concurrent Mode Failure 失败后的后备收集器使用
-XX:+UseParallelGC VM 运行在 Server 下的默认值,打开开关后,使用 Parallel Scavenge + Serial Old 的收集器组合进行内存回收
-XX:+UseParallelOldGC 打开开关后,使用 Parallel Scavenge + Parallel Old 的收集器组合进行内存回收
-XX:SurvivorRatio=ratio 新生代中 Eden 区与 Survivor 区的比值,默认为 8,表示 Eden:S0:S1 = 8:1:1
-XX:MaxTenuringThreshold=threshold 晋升到老年代的对象年龄。当前最大值为15。并行收集器的默认值为15,而CMS的默认值为4。
-XX:ParallelGCThreads=threads 设置新旧并行垃圾收集器中的垃圾收集线程数。缺省值随运行JVM的平台而异。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章