Java虚拟机JVM
JVM内存模型
1、线程私有
1.1、Java虚拟机栈:Java方法执行的内存模型
- 生命周期:与线程相同
- 执行时创建栈帧用于存储:局部变量表、操作数栈、动态连接、方法出口等
- 局部变量量表:存放编译器可知的各种基本数据类型、对象引用和returnAddress类型
- 操作数栈:存放要操作的数
- 动态连接:对应代码内存入口地址
- 方法出口:方法调用或异常返回地址
对象引用:对象存储在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、异常
| 数据区 | StackOverflowError | OutOfMemoryError |
| 方法区 | NA | 方法区无法满足内存分配需求时; |
| Java堆 | NA | 堆中没有内存完成实例分配,并且堆也无法再扩展时; |
| 虚拟机栈 | 线程请求的栈深度大于虚机所允许的深度时; | 扩展时无法申请到足够的内存时; |
| 本地方法栈 | 线程请求的栈深度大于虚机所允许的深度时; | 扩展时无法申请到足够的内存时; |
| 程序计数器 | NA | NA |
垃圾回收
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、类加载器
- BootstrapClassLoader:启动类加载器,
- 加载<JAVA_HOME>\lib目录下,或者被-Xbootclasspath参数所指定的路径下并且是虚拟机识别的类库,加载到虚拟机内存中;
- ExtClassLoader:扩展类加载器,
- 加载<JAVA_HOME>\lib\ext目录下,或者被java.ext.dirs系统变量所指定的类库
- 开发者可以直接使用ExtClassLoader
- 加载器由sun.misc.Launcher$ExtClassLoader实现
- AppClassLoader:应用程序加载器,也叫系统类加载器
- 加载器由sun.misc.Launcher$ExtClassLoader实现
- 开发者可以直接使用AppClassLoader
- UserClassLoader
1.3、JDK双亲委派机制
- 每个类加载器,对加载过的类有缓存
- 向上委托查找,向下委托加载