JVM知识点总结

发布于:2024-05-19 ⋅ 阅读:(85) ⋅ 点赞:(0)

一、JVM结构

  • 类加载子系统
    • JVM类加载器分为3层,核心类加载器(Bootstrap类加载器),加载%JAVA_HOME%/lib包下的核心类;扩展类加载器(Extension类加载器) ,加载%JAVA_HOME%/lib/ext内的扩展类;应用类加载器(Application类加载器),加载应用程序内定义的类。
    • 双亲委派:加载类时先判断Bootstrap类加载器是否加载,未加载则判断扩展类加载器是否加载,都未加载则加载应用程序内部的类。优点:①保护核心类;②避免重复加载。
  • 执行引擎
  • 运行时数据区
    运行时数据区包含:
    • 线程共享区域:
      • 堆区: 动态扩展的数据结构。堆区特点:
        ①线程共享,需要考虑并发问题
        ②动态扩展的内存区域;
        ③被垃圾回收器监控回收;
        ④存储的对象都有一个指向它的类引用指针;
        ⑤分为年轻代和老年代,年轻代又分为Eden和两个Survivor(幸存者)区域,默认比例:8:1:1;
        ⑥按照对象创建的时间顺序分配内存,标记清除算法回收垃圾会产生大量内存碎片导致频繁GC。
      • 方法区: JDK7之前通过永久代实现,使用JVM内存,容易OOM;JDK8使用元空间实现,使用JVM外部内存。
      • 直接内存区域: 由操作系统直接操作的内存区域。优点:①零拷贝②不归Java堆内存管理,降低GC压力。缺点:①不归Java堆内存管理,容易内存泄漏,要注意释放②直接内存的分配和释放比Java堆上的操作成本更高。
    • 线程私有区域:
      • 程序计数器: 记录当前线程执行到的位置,线程切换需要使用。
      • 虚拟机栈: 用于存储方法调用和局部变量。栈帧: 局部变量表、帧数据区、操作数栈、动态链接。
      • 本地方法栈: native方法的方法调用和局部变量。

二、 如何判断对象是否存活

  • 引用计数法: 引用一次计数+1,简单快速,但出现互相引用时会导致无法回收。
  • 可达性分析法: 以GC Roots为起点,开始向下遍历,能够被访问到的对象认为是存活的,无法到达的对象认为垃圾对象等待回收。
    GC Roots:①静态变量②常量③虚拟机栈引用的对象④本地方法栈引用的对象。

三、垃圾回收算法

  • 标记复制: 使用双倍内存,将存活对象移动到另一内存区域,未存活对象回收,然后交换区域。特点:速率快,使用内存只有总内存的一半,常用于年轻代垃圾回收。
  • 标记清除: 标记后直接回收为存活对象,会产生大量内存碎片,导致频繁GC。速率中等,常用于老年代垃圾回收。
  • 标记整理: 过程:①从GCRoots出发标记存活对象;②将存活对象移动到一端,同时清理未标记的数据;③更新存活对象引用,使其指向对象所在新位置的地址;④回收阶段:回收掉所有未被标记的对象,释放内存(整理阶段的回收可能存在遗漏,如错标,所以需要回收阶段再统一回收)。速率慢,减少内存碎片,减少内存分配失败的情况(内存不连续)。

四、三色标记法

将对象标记为三种颜色:白色、灰色和黑色,用于表示对象的可达性状态。

  • 白色:所有对象初始状态都为白色,表示尚未被访问和处理。
  • 灰色:对象被标为灰色表示这些对象已经访问,但其引用的对象还未被遍历。
  • 黑色:对象被标记为黑色表示这些对象已经被访问,且其引用的对象已经被遍历。

基本步骤:
①初始化:根对象标记为灰色,其他对象标记为白色;
②标记阶段:从根对象开始遍历对象,遍历完成的对象标记为黑色;
③清除阶段:清除所有未被标记为黑色的对象,这些对象被认为不可达,可以回收。

五、四大引用类型

  • 强引用: 垃圾回收时,即使系统内存不足,也不会回收该对象,如new的对象。
  • 软引用: 垃圾回收时,系统内存充足不会回收,系统内存不足时会被回收,适合缓存功能。
  • 弱引用: 垃圾回收时,系统内存无论是否充足,都会被回收,ThreadLocal 中 Key。
  • 虚引用: 相当于不存在,垃圾回收时会得到通知。

六、yangGC流程

  1. yangGC发生在年轻代,年轻代区域分为1个Eden区和两个幸存区(Survivor),比例8:1:1,幸存区分为from/to两个角色;
  2. Eden区三色标记法判断对象存活,然后通过标记复制算法垃圾回收,Eden区存活对象移动到to区域;
  3. From区域存储的上一次yangGC存活的对象,根据存活对象的存活年代判断是否移动到老年代,未移动到老年代的对象移动到To区域;
  4. 交换From和To区域的引用,保证To区域GC完成后为空;
  5. yangGC完成后如果新创建对象需要的内存仍不够,会触发空间担保机制将存活对象从新生代晋升到老年代;
  6. 循环上述过程,To区域被填满后所有对象移动到老年代。

七、主流垃圾回收算法

  • CMS: 特点:基于标记清除算法,并发收集、低停顿。工作机制:
    ①初始标记:标记GC Roots直接关联的对象,速度很快,需要暂停所有工作线程;
    ②并发标记:遍历GC Roots引用链,并发进行,不需要暂停工作线程;
    ③重新标记:修正并发标记期间,因用户线程继续工作而导致产生变动的一部分对象的标记记录,需要暂停所有工作线程;
    ④并发清除:清除遍历GC Roots不可达的对象,并发进行,不需要暂停工作线程。
  • Parallel: :多线程垃圾回收器,基于标记整理算法。
  • G1: JDK9之后默认的垃圾回收器,基于标记整理算法。
  • CMS与Parallel区别:
    ①使用场景不同,CMS适合对停顿时间要求较高的场景,Parallel适合对吞吐量要求较高的场景;
    ②工作原理不同,CMS 老年代垃圾回收,基于标记清除算法,会产生内存碎片导致频繁GC,Parallel 老年代垃圾回收,基于标记整理算法,不会产生大量内存碎片;
    ③CMS停顿时间较短,Parallel停顿时间较长,停顿时无法正常提供服务。

八、OOM

  • 产生的原因:
    ①堆区分配的内存不足;
    ②堆区内存泄漏,多余内存无法正常释放,代码有问题;
    ③栈内存不足,虚拟机在扩展栈的时候无法申请到足够的内存;
    StackOverflowError :线程请求的栈深度超过了虚拟机允许的最大深度,会抛出该异常(无限递归)。

九、常用工具

jps:输出JVM中运行的进程状态信息。
jstack:生成虚拟机当前时刻的线程快照。
jstat:虚拟机统计信息监控工具。
jinfo:实时查看和调整虚拟机的各项参数。
jmap:生成虚拟机的内存转存储快照。
JConsole:可视化监控工具。

十、如何进行JVM调优

  1. 全面监控,收集性能数据,分析GC日志,料及GC类型、频率、停顿时间等信息;
  2. 如果存在内存泄漏,则下载dump文件,利用工具分析内存泄漏对象关联的GC Roots,优化对应代码;
  3. 根据业务场景和需求选择合适的GC算法,如CMS、Parallel、G1等;
  4. 设置JVM参数,如初始堆内存大小、最大堆内存大小、新生代大小等;
  5. 优化代码;
  6. 根据需求,压测应用,观察吞吐量、性能、GC频率、内存大小、GC时间等,不断调整JVM参数到合适值,确保调优后达到性能和稳定性要求。
  • 一般情况下,yangGC单次小于50ms且间隔在10ms以上,表示健康;
    FullGC单次小于1s,且间隔在10min以上,表示健康。