Java虚拟机JVM

📅 2026/7/14 7:08:20 👁️ 阅读次数 📝 编程学习
Java虚拟机JVM

JVM内存模型

1、线程私有

1.1、Java虚拟机栈:Java方法执行的内存模型

  • 生命周期:与线程相同
  • 执行时创建栈帧用于存储:局部变量表、操作数栈、动态连接、方法出口等
  1. 局部变量量表:存放编译器可知的各种基本数据类型、对象引用和returnAddress类型
  2. 操作数栈:存放要操作的数
  3. 动态连接:对应代码内存入口地址
  4. 方法出口:方法调用或异常返回地址

对象引用:对象存储在Java堆中,这里存的时对象的引用

1.2、本地方法栈

native修饰的方法

private native void start0();

1.3、程序计数器

  • 当执行Java方法时:正在执行的虚拟机字节码地址;
  • 当执行Native方法时:Undefined

2、共享区域

2.1、Java堆

  • 存放对象实例,GC的主要区域。
  • 新生代、老年代。
  • Eden区、From Surivivor区、To Surivivor区。8:1:1

2.2、方法区

用于存储已被虚机加载的类信息、常量、静态变量、即时编译器编译后的代码等。

3、异常

运行时数据区对应异常
数据区StackOverflowErrorOutOfMemoryError
方法区NA方法区无法满足内存分配需求时;
Java堆NA堆中没有内存完成实例分配,并且堆也无法再扩展时;
虚拟机栈线程请求的栈深度大于虚机所允许的深度时;扩展时无法申请到足够的内存时;
本地方法栈线程请求的栈深度大于虚机所允许的深度时;扩展时无法申请到足够的内存时;
程序计数器NANA

垃圾回收

1、GC RootsJVM中被称为GC Roots的对象,是指垃圾回收器在进行可达性分析时,作为起始节点的“活跃”对象。凡是通过直接或间接引用链能够与 GC Roots 相连的对象,都会被判定为“存活”;反之则被视为垃圾等待回收。

在Java中,以下6种类型的对象通常可以作为 GC Roots:

  • 虚拟机栈引用的对象
    当前正在执行的方法中使用的局部变量表、参数和临时对象。
  • 本地方法栈引用的对象
    在调用 Native 方法(使用 C/C++ 等编写的代码)时引用的对象。
  • 方法区中静态属性引用的对象
    类变量,即被static关键字修饰的对象引用。
  • 方法区中常量引用的对象
    字符串常量池里的引用或被final修饰的常量。
  • 同步锁持有的对象
    synchronized关键字锁住的对象。
  • JNI 句柄引用的对象
    通过 Java Native Interface 引用的本地对象。

2、对象是否存活

2.1、引用计数器算法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就+1;当引用失效时,计数器就-1;当计数器值为0时,对象就不再被使用;

缺陷:当出现对象相互循环引用时,永远不会被回收;

2.2、可达性分析算法

2.3、引用

2.3.1、强引用

只要强引用存在,垃圾回收器绝对不会回收被引用的对象。

Object obj = new Object();
2.3.2、软引用

只有当 JVM内存不足(即将抛出 OOM 前),垃圾回收器才会将这些对象列入回收范围进行二次回收。

String str = new String("abc"); SoftReference<String> reference = new SoftReference(str);
2.3.3、弱引用

比软引用更弱。无论内存是否充足,只要垃圾回收器运行,只要发现只被弱引用关联的对象,就会立即回收

String str = new String("abc"); WeakReference<String> reference = new WeakReference(str);
2.3.4、虚引用

任何时候都可能被垃圾回收器回收

String str = new String("abc"); ReferenceQueue queue = new ReferenceQueue(); // 创建虚引用,要求必须与一个引用队列关联 PhantomReference<String> reference = new PhantomReference(str, queue);

3、垃圾收集算法

算法优点弊端
标记-清除算法基础算法,实现简单;不需要移动对象

1、标记和清除2个效率都不高

2、空间不连续,空间碎片

复制算法不会产生内存碎片浪费一半的空间
标记-整理算法

1、不会产生内存碎片

2、内存利用率高

在移动对象时,必须STW
分代收集算法资源利用率高,针对不同区域采用最合适的算法(如新生代用复制算法,老年代用标记-整理算法)算法逻辑相对复杂

4、垃圾收集器

JVM 垃圾收集器主要分为分代收集器(如 Parallel、CMS、G1)和全功能/并发收集器(如 ZGC)。不同收集器的优缺点决定了其应用场景:追求高吞吐量选 Parallel,追求低延迟选 CMS 或 G1,追求超大内存/极致低延迟选 ZGC。

常见垃圾收集器优缺点
收集器名称工作区域优点缺点
Serial / Serial Old新生代 / 老年代单线程,内存消耗小,没有线程交互开销。GC 时需暂停所有用户线程(STW),垃圾回收时间长。
Parallel Scavenge / Parallel Old新生代 / 老年代多线程并行回收,追求高吞吐量(CPU利用率高)。GC 停顿时间较长,不适合强交互的低延迟应用。
CMS(Concurrent Mark Sweep)老年代并发收集、低停顿,用户线程与 GC 线程同时执行。产生内存碎片,对 CPU 资源敏感,无法处理“浮动垃圾”。
G1(Garbage-First)全堆(分代管理)可预测停顿时间,将堆内存划分为多个 Region,避免了全区碎片化。内存占用高,程序运行时的负载与复杂度较高。
ZGC(Z Garbage Collector)全堆(不分代/自适应)超低延迟(停顿 <10ms),支持超大堆(数 TB),几乎不受堆大小影响。吞吐量相比 Parallel 略低,处于持续演进中。

5、内存分配和回收策略

JVM参数

JVM参数主要分为标准参数(-)、非标准参数(-X)和不稳定参数(-XX)。合理配置这些参数能够显著提升应用程序的性能、内存管理与故障排查效率。以下为您整理了生产环境中必不可少的常用JVM参数分类清单。

1、 堆内存与非堆内存配置

控制Java应用运行时的内存分配,是性能调优的第一步。

  • -Xms:设置JVM启动时的初始堆内存(建议与-Xmx一致,避免内存抖动)。
  • -Xmx:设置JVM最大可用堆内存。
  • -Xmn:设置年轻代(Young Generation)的大小。
  • -XX:MetaspaceSize:设置元空间(Metaspace)的初始大小(触发首次Full GC的阈值)。
  • -XX:MaxMetaspaceSize:限制元空间的最大大小,防止内存泄漏耗尽系统内存。
  • -XX:SurvivorRatio:设置Eden区与单个Survivor区的大小比例(如8表示Eden:S0:S1 = 8:1:1)。

2、 垃圾收集器(GC)策略

根据业务场景选择合适的GC算法,以平衡吞吐量和停顿时间。

  • -XX:+UseParallelGC:JDK 8 默认的并行GC,注重吞吐量。
  • -XX:+UseG1GC:JDK 9及之后版本的默认GC,兼顾吞吐量与低延迟。
  • -XX:+UseZGC:适用于超大堆内存、需要极低延迟(亚毫秒级)的现代应用。
  • -XX:MaxGCPauseMillis:在使用G1或ZGC时,设置期望的最大GC停顿时间。

3、 异常与故障排查

在生产环境发生OOM(内存溢出)时,通过这些参数留存现场数据。

  • -XX:+HeapDumpOnOutOfMemoryError:当首次发生OOM时,自动生成 Heap Dump 内存快照文件。
  • -XX:HeapDumpPath:指定 Heap Dump 文件的生成路径和文件名。
  • -XX:ErrorFile:指定JVM致命错误日志(如 Crash 日志)的存储路径。

4、 GC日志记录

记录详细的GC运行情况,用于排查内存泄漏及优化GC停顿。

  • -Xlog:gc*(JDK 9+):开启统一的GC日志记录,输出详细信息。
  • -XX:+PrintGCDetails(JDK 8及以下):打印详细的GC日志信息。
  • -Xloggc(JDK 8及以下):指定GC日志的输出文件路径。

5、 其他常用优化参数

  • -XX:+DisableExplicitGC:屏蔽业务代码中显式的System.gc()调用,防止引发人为的Full GC。
  • -Xss:设置每个线程的栈内存大小(例如512k),决定了函数调用的最大深度。
  • -XX:+UseStringDeduplication:启用字符串去重,优化存在大量重复字符串的应用内存占用(Java 8u20及以上支持)。

优化实战

1、双11亿级电商网站JVM参数调优

2、单机几十万并发的系统JVM调优

虚拟机类加载机制

1、类加载器

1.1、类加载的时机

类从被加载到虚拟机内存中开始,到卸载出内存为止,整个生命周期包括5个阶段

  1. 加载
  2. 连接:验证 + 准备 + 解析
  3. 初始化
  4. 使用
  5. 卸载

1.2、类加载器

  1. BootstrapClassLoader:启动类加载器,
    1. 加载<JAVA_HOME>\lib目录下,或者被-Xbootclasspath参数所指定的路径下并且是虚拟机识别的类库,加载到虚拟机内存中;
  2. ExtClassLoader:扩展类加载器,
    1. 加载<JAVA_HOME>\lib\ext目录下,或者被java.ext.dirs系统变量所指定的类库
    2. 开发者可以直接使用ExtClassLoader
    3. 加载器由sun.misc.Launcher$ExtClassLoader实现
  3. AppClassLoader:应用程序加载器,也叫系统类加载器
    1. 加载器由sun.misc.Launcher$ExtClassLoader实现
    2. 开发者可以直接使用AppClassLoader
  4. UserClassLoader

1.3、JDK双亲委派机制

  1. 每个类加载器,对加载过的类有缓存
  2. 向上委托查找,向下委托加载