八股-JVM
JVM 相关面试题
Java Virtual Machine, Java程序的运行环境(java二进制字节码的运行环境)

======= JVM 组成 =======
什么是程序计数器
程序计数器:线程私有的,内部保存的字节码的行号。用于记录正在执行的字节码指令的地址
Java 堆

线程共享区域:主要用于保存对象实例、数组等。当堆中内存空间不足以分配给实例,且无法进一步扩展时,将抛出OutOfMemoryError
异常
组成:年轻代+老年代
年轻代被划分为三部分,即Eden区和两个大小严格相同的Survivor区。根据虚拟机(VM)的策略,在经过几次垃圾收集后,仍然存活于Survivor区的对象将被移动到老年代区间
老年代主要保存生命周期较长的对象,通常是一些老旧的对象
元空间用于保存类信息、静态变量、常量和编译后的代码
Jdk1.7 和 1.8 的区别
1.7版本中存在一个永久代,该代用于存储类信息、静态变量、常量以及编译后的代码。
1.8版本移除了永久代,并将数据存储于本地内存的元空间中,以此防止内存溢出
什么是虚拟机栈
Java虚拟机栈(Java Virtual Machine Stacks)
每个线程运行时所需的内存,称为虚拟机栈,遵循先进后出的原则
每个栈由多个栈帧(frame)组成,这些栈帧对应着每次方法调用时所占用的内存。每个线程仅能有一个活动栈帧,该栈帧对应着当前正在执行的方法
垃圾回收是否涉及栈内存?
垃圾回收主要针对的是堆内存。当栈帧弹出后,内存便会得到释放
栈内存分配越大越好吗?
并非如此。默认的栈内存通常为1024KB。栈帧过大可能会导致线程数减少。例如,若机器总内存为512MB,当前可活动的线程数则为512个。若将栈内存调整为2048KB,那么可活动的栈帧数量将减半
方法内的局部变量是否线程安全?
- 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的
- 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
sb.append(1);
sb.append(2);
new Thread(() -> {
m2(sb);
}).start();
}
public static void m1(){
StringBuilder sb = new StringBuilder();
sb.append(1);
sb.append(2);
System.out.println(sb.toString());
}
// 线程安全
public static void m2(StringBuilder sb){
sb.append(3);
sb.append(4);
System.out.println(sb.toString());
}
// 线程不安全
public static StringBuilder m3(){
StringBuilder sb = new StringBuilder();
sb.append(5);
sb.append(6);
return sb;
}
// 线程不安全
栈内存溢出情况
栈帧过多导致栈内存溢出,典型问题:递归调用
栈帧过大导致栈内存溢出
方法区
方法区(Method Area)是各个线程共享的内存区域,主要存储类的信息、运行时常量池。虚拟机启动时创建,关闭虚拟机时释放。若方法区域内存无法满足分配请求,则将抛出OutOfMemoryError
(Metaspace)
位置:
jdk7 之前在堆中
jdk8 及之后在本地内存的元空间中(默认无大小上限)
设置大小:
-XX:MaxMetaspaceSize=8m
常量池
可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
javap -v Application.class
查看字节码结构(类的基本信息、常量池、方法定义)

运行时常量池
常量池位于.class
文件中。当该类被加载时,其常量池信息将被放入运行时常量池,并将其中的符号地址转换为真实地址
直接内存
直接内存:并不属于VM中的内存结构,不由VM进行管理。它是虚拟机的系统内存,常见于NO
操作时,用于数据缓冲区。其分配回收成本较高,但读写性能优越
举例:Java代码完成文件拷贝
常规 IO 的数据拷贝流程

NIO 数据拷贝流程
少了一次从系统缓冲区到 java 缓冲区的拷贝

======= 类加载器 =======
什么是类加载器,类加载器有哪些
在 Java 中,类加载器(ClassLoader) 是负责将 .class 字节码文件加载到 JVM 内存中 的一个核心组件
类加载器(ClassLoader) 的作用就是:
将 .class 文件从文件系统、网络或其他位置读取,并将其转换为 JVM 可以使用的 Class 对象
它是 Java 动态加载机制 的基础,使得 Java 可以在运行时加载类,而不是编译时就全部固定
Java 中主要有以下几种 类加载器:
名称 | 类名 | 加载内容 | 说明 |
---|---|---|---|
启动类加载器 | BootstrapClassLoader | 加载 JDK 核心类jre/lib/ext/,如java.lang.*、java.util.*等 | C++ 实现,不是 Java 类,看不到源码 |
扩展类加载器 | ExtClassLoader/PlatformClassLoader(Java 9+) | 加载ext目录下的类,如jre/lib/ext/ | Java 实现,父类是ClassLoader |
应用类加载器(系统类加载器) | AppClassLoader | 加载classpath下的类,也就是我们自己写的应用代码 | 最常用的类加载器 |
自定义类加载器 | 继承ClassLoader自己实现 | 加载指定路径或加密类等 | 常用于热部署、插件、框架等场景 |

类加载器的双亲委派模型(Parent Delegation Model)
Java 的类加载器采用 双亲委派机制:
先让父类加载器尝试加载,只有当父类加载器无法加载时,子类加载器才会尝试加载
好处:
- 避免重复加载
- 保证核心类不会被覆盖,防止安全问题
- 保证 Java 核心 API 的一致性
流程如下:
类加载请求
↓
父加载器(Bootstrap)
↓
父加载失败 → 子加载器尝试
查看当前类是由哪个类加载器加载的
System.out.println(String.class.getClassLoader()); // null(BootstrapClassLoader)
System.out.println(MyClass.class.getClassLoader()); // sun.misc.Launcher$AppClassLoader
自定义类加载器(简单例子)
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 从文件或网络中读取字节数组,然后 defineClass
byte[] bytes = ...;
return defineClass(name, bytes, 0, bytes.length);
}
}
实际应用场景
场景 | 类加载器的作用 |
---|---|
Spring Boot | 实现隔离不同模块的类加载 |
Tomcat / OSGi | 支持多 web 应用独立加载类 |
热部署 | 动态替换类的定义 |
插件系统 | 每个插件使用自己的类加载器 |
类装载的执行过程

加载
通过类的全名,获取类的二进制数据流
解析类的二进制数据流为方法区内的数据结构(Java类模型)
创建java.lang.Class
类的实例,表示该类型。作为方法区,此类的各种数据提供访问入口

验证
验证类是否符合JVM规范,安全性检查
- 文件格式验证
- 元数据验证
- 字节码验证
格式检查,如:文件格式是否错误、语法是否错误、字节码是否合规
- 符号引用验证 Class 文件在其常量池会通过字符串记录自己将要使用的其他类或者方法,检查它们是否存在
准备
为类变量分配内存并设置其初始值:
-
static
变量,其空间分配在准备阶段完成(设置默认值) ,赋值操作则在初始化阶段完成 - 当
static
变量是基本类型且为final
,或为字符串常量时,其值已确定,赋值操作在准备阶段完成 - 若
static
变量是final
的引用类型,赋值操作同样会在初始化阶段完成
解析
将类中符号引用转换为直接引用
例如:方法内调用其他方法时,方法名可视为符号引用;而直接引用则是通过指针直接指向方法

初始化
对类的静态变量与静态代码块执行初始化操作
若在初始化一个类时,其父类尚未完成初始化,则系统将优先初始化其父类
若一个类中同时包含多个静态变量和静态代码块,这些代码将按照自上而下的顺序依次执行
使用
JVM从入口方法开始执行用户程序代码
调用静态类成员信息(例如:静态字段、静态方法)
使用
new
关键字为其创建对象实例
卸载
======= 垃圾回收 =======
对象什么时候可以被垃圾回收器回收
引用计数法
一个对象被引用了一次,在当前的对象头上递增一次引用次数,如果这个对象的引用次数为0,代表这个对象可回收
失效:
对象间循环引用导致引用计数失效
public class Demo {
Demo instance;
String name;
public Demo(String name){
this.name = name;
}
}
Demo a = new Demo("a");
Demo b = new Demo("b");
a.instance = b;
b.instance = a;
a = null;
b = null;


可达性分析法
现在的虚拟机采用的都是通过可达性分析算法来确定哪些内容是垃圾
扫描堆中的对象,看是否能够沿着GC Root对象起点的引用链找到该对象。若找不到,则表示可以回收
X、Y这两个节点是可回收的
哪些对象可以作为 GC Root
虚拟机栈(栈帧中的本地变量表)所引用的对象
方法区中类静态属性所引用的对象
方法区中常量所引用的对象
本地方法栈中Native方法(JNI)所引用的对象
public class GCRootDemo {
// ① 静态变量(方法区中)
private static MyObject staticObj = new MyObject("static");
// ② 常量引用(方法区常量池)
private static final MyObject constantObj = new MyObject("constant");
public static void main(String[] args) {
// ③ 局部变量(栈帧中的本地变量表)
MyObject localObj = new MyObject("local");
}
}
垃圾回收算法
标记清除算法
标记清除算法,将垃圾回收过程分为两个阶段:标记与清除
- 根据可达性分析算法,对垃圾进行标记
- 对标记为可回收的内容执行垃圾回收操作

优点:标记与清除速度较快
缺点:碎片化现象较为严重,内存连贯性不足
标记整理算法

优缺点与标记清除算法相似,解决了标记清除算法的碎片化问题。同时,标记压缩算法增加了一步对象移动内存位置的步骤,这对效率产生了一定的影响
复制算法
将内存分成两个大小相等的区域

优点:在垃圾对象多的情况下,效率较高、清理后,内存无碎片
缺点:分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低
JVM 的分代回收
堆的划分
在Java 8中,堆内存被划分为两个部分:新生代与老年代【1:2】

新生代内部划分为三个区域:
- 伊甸园区(Eden):新生对象均分配至此
- 幸存者区(Survivor):分为from区和to区
- 区域分配比例为Eden区:from区:to区 = 8:1:1
分代收集算法-工作机制

新创建的对象,均会首先分配至 Eden 区
当 Eden 内存不足时,会标记 Eden 区与 from(现阶段尚无)中的存活对象
随后,采用复制算法将这些存活对象复制至 to 区。复制完成后,Eden 区与 from 区的内存均得到释放

经过一段时间,伊甸园内存再次出现不足。标记eden区域中的to区存活对象,并将这些存活对象复制至from区

当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足或大对象会导致提前晋升)

Minor GC、Mixed GC、Full GC的区别

- Minor GC【年轻代GC】发生在新生代的垃圾回收,暂停时间短(STW)
- Mixed GC:新生代与老年代部分区域的垃圾回收,G1收集器特有
- Full GC:新生代与老年代完整垃圾回收,暂停时间长(STW),应尽力避免
JVM 垃圾回收器
串行垃圾收集器
Serial与Serial Old串行垃圾收集器,指采用单线程进行垃圾回收,适用于堆内存较小的个人电脑
Serial作用于新生代,采用复制算法;Serial Old作用于老年代,采用标记-整理算法
垃圾回收过程中,仅有一个线程在工作,同时Java应用中的所有线程均需暂停(STW),直至垃圾回收完成

并行垃圾收集器
Parallel New与Parallel Old均为并行垃圾回收器,JDK 8默认采用此回收器
Parallel New针对新生代,运用复制算法
Parallel Old针对老年代,运用标记-整理算法
在垃圾回收过程中,多线程协同工作,同时Java应用中的所有线程均需暂停(STW),直至垃圾回收完成

CMS(并发)垃圾收集器
CMS全称Concurrent Mark Sweep,是一款并发式、采用标记-清除算法的垃圾回收器。该回收器专门针对老年代垃圾回收,旨在实现最短的回收停顿时间。停顿时间越短,用户体验越佳。其显著特点在于,在执行垃圾回收过程中,应用程序仍可正常运行

G1垃圾收集器
G1垃圾收集器
- 应用于新生代和老年代,在JDK9之后默认使用G1
- 划分成多个区域,每个区域都可以充当 eden, survivor, old, humongous,其中 humongous 专为大对象准备
- 采用复制算法
- 响应时间与吞吐量兼顾
- 分成三个阶段:新生代回收、并发标记、混合收集
- 如果并发失败(即回收速度赶不上创建新对象速度),会触发 Full GC
年轻代垃圾回收
初始时,所有区域均处于空闲状态
创建了若干对象,从中挑选一些空闲区域作为伊甸园区以存储这些对象
当伊甸园执行垃圾回收操作时,选出一个空闲区域作为幸存区。此时,采用复制算法对存活对象进行复制,此过程将导致用户线程的暂停(STW,但是时间较短)
随着时间流逝,伊甸园的内存又不足
将伊甸园以及之前幸存区中的存活对象,采用复制算法,复制到新的幸存区,其中较老对象晋升至老年代
年轻代垃圾回收+并发标记
当老年代占用内存超过阈值(默认是45%)后,触发并发标记,这时无需暂停用户线程
并发标记完成后,将进入重新标记阶段以解决漏标问题,此阶段需暂停用户线程
完成这些步骤后,即可明确老年代中存活的对象。随后,系统将进入混合收集阶段。在此阶段,并不会对所有老年代区域进行回收,而是根据暂停时间目标,优先回收价值高(存活对象少)的区域。这亦是“垃圾优先”(Garbage First)名称的由来
混合垃圾回收
混合收集阶段中,参与复制的有eden、survivor、old

复制完成,内存得到释放。进入下一轮的新生代回收、并发标记、混合收集

强引用、软引用、弱引用、虚引用
引用类型与回收机制:
- 强引用:只要所有GC Roots能找到,就不会被回收
- 软引用:需配合
SoftReference
使用,当垃圾回收多次执行,内存依然不够时,会回收软引用对象 - 弱引用:需配合
WeakReference
使用,只要进行垃圾回收,就会把弱引用对象回收 - 虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由
Reference Handler
线程调用虚引用相关方法释放直接内存
在 Java 中,对象引用的强度影响垃圾回收器对它们的处理方式
💪 1. 强引用(Strong Reference)
✅ 特性:
- 默认的引用类型(最常用)
- 只要强引用存在,垃圾回收器永远不会回收该对象
📌 用途:
- 程序中绝大多数的对象使用强引用管理
🧪 示例:
Object obj = new Object(); // obj 是一个强引用
🧽 2. 软引用(Soft Reference)
✅ 特性:
- 内存不足时才会被 GC 回收
- 适合做缓存,内存够用就保留,内存紧张就释放
📌 用途:
- 实现内存敏感的缓存,如图片缓存、对象缓存等
🧪 示例:
SoftReference<Object> softRef = new SoftReference<>(new Object());
Object obj = softRef.get(); // 获取引用对象
🐜 3. 弱引用(Weak Reference)
✅ 特性:
- 只要 GC 发生,就会被回收
- 存活时间比软引用更短
📌 用途:
- 实现 ThreadLocal 的内部清理
- 使用在对象生命周期控制较短的地方,如 WeakHashMap
🧪 示例:
WeakReference<Object> weakRef = new WeakReference<>(new Object());
Object obj = weakRef.get(); // 获取引用对象
👻 4. 虚引用(Phantom Reference)
✅ 特性:
- 对象被回收前,虚引用会进入引用队列(ReferenceQueue)
- 调用 get() 永远返回 null
- 主要用于在对象被 GC 回收时执行清理动作
📌 用途:
- 管理堆外内存,如 DirectByteBuffer
- 替代 finalize() 进行资源释放

🧪 示例:
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
🧠 总结对比
类型 | 是否影响 GC 回收 | 回收时间点 | 常见用途 |
---|---|---|---|
强引用 | 否 | 永不 | 普通对象引用 |
软引用 | 是(内存不足时) | 内存紧张 | 缓存 |
弱引用 | 是(GC 时) | 一次 GC 过程 | 弱键 Map、ThreadLocal |
虚引用 | 是(GC 时) | 对象将被回收时 | 清理堆外资源 / 跟踪回收 |
======= 实践 =======
JVM 调优的参数可以在哪里设置
- war包部署在tomcat中设置
修改TOMCAT_HOME/bin/catalina.sh
文件

- jar包部署在启动参数设置
通常在Linux系统下,直接通过添加参数启动Spring Boot项目
nohup java -Xms512m -Xmx1024m -jar xxxx.jar --spring.profiles.active=prod &
JVM 调优参数有哪些
对于JVM调优,主要就是调整年轻代、老年代、元空间的内存空间大小及使用的垃圾回收器类型
设置堆空间大小
设置堆的初始大小与最大大小,为防止垃圾收集器在初始大小与最大大小之间进行收缩堆操作时产生额外时间,通常将最大和初始大小设置为相同的值
-
-Xms
:设置堆的初始化大小 -
-Xmx
:设置堆的最大大小
参数示例:
-
-Xms:1024
-
-Xms:1024k
-
-Xms:1024m
-
-Xms:1g
不指定单位默认为字节,指定单位,按照指定的单位设置
堆空间设置多少合适?
- 最大大小的默认值是物理内存的1/4,初始大小是物理内存的1/64
- 堆太小,可能会频繁的导致年轻代和老年代的垃圾回收,会产生STW,暂停用户线程
- 堆内存大肯定是好的,存在风险,假如发生了fullgc,它会扫描整个堆空间,暂停用户线程的时间长
- 设置参考推荐:尽量大,也要考察一下当前计算机其他程序的内存使用情况
虚拟机栈的设置
虚拟机栈的设置:每个线程默认会开启1M的内存,用于存放栈帧、调用参数、局部变量等,但一般256K就够用。通常减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统
-
-Xss
对每个线程 stack 大小的调整,例如-Xss128k
年轻代中Eden区和两个Survivor区的大小比例
设置年轻代中 Eden 区和两个 Survivor 区的大小比例。该值如果不设置,则默认比例为 8:1:1
通过增大 Eden 区的大小,来减少 YGC 发生的次数,但有时我们发现,虽然次数减少了,但 Eden 区满的时候,由于占用的空间较大,导致释放缓慢,此时 STW 的时间较长,因此需要按照程序情况去调优
参数示例:
-
-XX:SurvivorRatio=8
,表示年轻代中的分配比率:survivor:eden = 2:8
年轻代晋升老年代阈值
-XX:MaxTenuringThreshold=threshold
- 默认为 15
- 取值范围 0-15
设置垃圾回收收集器
通过增大吞吐量提高系统性能,可以通过设置并行垃圾回收收集器
-
-XX:+UseParallelGC
:使用并行垃圾收集器进行新生代回收 -
-XX:+UseParallelOldGC
:使用并行垃圾收集器进行老年代回收 -
-XX:+UseG1GC
:使用 G1 垃圾收集器
JVM 调优工具
命令工具
jsp 进程状态信息
(base) charmingdaidai@CharmingDaiDaideMac-mini ~ % jps
9633 RemoteMavenServer36
2168 org.eclipse.equinox.launcher_1.7.0.v20250331-1702.jar
76747 Jps
9133 Main
jstack 查看 java 进程内线程的堆栈信息
(base) charmingdaidai@CharmingDaiDaideMac-mini ~ % jstack 2168
2025-05-19 22:02:13
Full thread dump OpenJDK 64-Bit Server VM (21.0.6+7-LTS mixed mode, sharing):
Threads class SMR info:
_java_thread_list=0x0000000116561c50, length=24, elements={
0x0000000121008e00, 0x000000012181e600, 0x0000000133008800, 0x0000000106008200,
0x000000012100ec00, 0x000000012100f400, 0x000000012100ca00, 0x0000000121011e00,
0x0000000121820200, 0x0000000132034200, 0x0000000133010600, 0x000000013290a000,
0x000000013290a800, 0x0000000106049c00, 0x00000001330c6200, 0x0000000121214800,
0x0000000132ab6e00, 0x0000000132b36a00, 0x0000000132099800, 0x000000013180d800,
0x00000001060ae000, 0x0000000133164600, 0x00000001318b8800, 0x000000012125a000
}
"main" #1 [6147] prio=5 os_prio=31 cpu=672.21ms elapsed=117776.78s tid=0x0000000121008e00 nid=6147 in Object.wait() [0x000000016fe52000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.jdt.ls.core.internal.LanguageServerApplication.start(LanguageServerApplication.java:63)
- locked <0x00000007c1009080> (a java.lang.Object)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
at java.lang.invoke.DirectMethodHandle$Holder.invokeStatic([email protected]/DirectMethodHandle$Holder)
at java.lang.invoke.LambdaForm$MH/0x000000c801080800.invoke([email protected]/LambdaForm$MH)
at java.lang.invoke.LambdaForm$MH/0x000000c801002c00.invokeExact_MT([email protected]/LambdaForm$MH)
at jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl([email protected]/Unknown Source)
at jdk.internal.reflect.DirectMethodHandleAccessor.invoke([email protected]/Unknown Source)
at java.lang.reflect.Method.invoke([email protected]/Unknown Source)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:702)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:646)
at org.eclipse.equinox.launcher.Main.run(Main.java:1577)
at org.eclipse.equinox.launcher.Main.main(Main.java:1549)
"Reference Handler" #5 [23043] daemon prio=10 os_prio=31 cpu=6.58ms elapsed=117776.67s tid=0x000000012181e600 nid=23043 waiting on condition [0x000000017079a000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList([email protected]/Native Method)
at java.lang.ref.Reference.processPendingReferences([email protected]/Unknown Source)
at java.lang.ref.Reference$ReferenceHandler.run([email protected]/Unknown Source)
"Finalizer" #6 [23555] daemon prio=8 os_prio=31 cpu=0.08ms elapsed=117776.67s tid=0x0000000133008800 nid=23555 in Object.wait() [0x00000001709a6000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <0x00000007c06f7950> (a java.lang.ref.NativeReferenceQueue$Lock)
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.ref.NativeReferenceQueue.await([email protected]/Unknown Source)
at java.lang.ref.ReferenceQueue.remove0([email protected]/Unknown Source)
at java.lang.ref.NativeReferenceQueue.remove([email protected]/Unknown Source)
- locked <0x00000007c06f7950> (a java.lang.ref.NativeReferenceQueue$Lock)
at java.lang.ref.Finalizer$FinalizerThread.run([email protected]/Unknown Source)
"Signal Dispatcher" #7 [23811] daemon prio=9 os_prio=31 cpu=0.12ms elapsed=117776.67s tid=0x0000000106008200 nid=23811 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" #8 [30723] daemon prio=9 os_prio=31 cpu=9.11ms elapsed=117776.67s tid=0x000000012100ec00 nid=30723 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Deflation Thread" #9 [30211] daemon prio=9 os_prio=31 cpu=8724.94ms elapsed=117776.67s tid=0x000000012100f400 nid=30211 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #10 [29955] daemon prio=9 os_prio=31 cpu=9727.02ms elapsed=117776.67s tid=0x000000012100ca00 nid=29955 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"C1 CompilerThread0" #13 [24579] daemon prio=9 os_prio=31 cpu=3963.81ms elapsed=117776.67s tid=0x0000000121011e00 nid=24579 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"Common-Cleaner" #14 [29443] daemon prio=8 os_prio=31 cpu=239.54ms elapsed=117776.66s tid=0x0000000121820200 nid=29443 waiting on condition [0x00000001715ee000]
java.lang.Thread.State: TIMED_WAITING (parking)
at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
- parking to wait for <0x00000007c08204a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos([email protected]/Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await([email protected]/Unknown Source)
at java.lang.ref.ReferenceQueue.await([email protected]/Unknown Source)
at java.lang.ref.ReferenceQueue.remove0([email protected]/Unknown Source)
at java.lang.ref.ReferenceQueue.remove([email protected]/Unknown Source)
at jdk.internal.ref.CleanerImpl.run([email protected]/Unknown Source)
at java.lang.Thread.runWith([email protected]/Unknown Source)
at java.lang.Thread.run([email protected]/Unknown Source)
at jdk.internal.misc.InnocuousThread.run([email protected]/Unknown Source)
"Notification Thread" #15 [28419] daemon prio=9 os_prio=31 cpu=0.04ms elapsed=117776.39s tid=0x0000000132034200 nid=28419 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Active Thread: Equinox Container: c012ab8d-a389-43fb-bf67-3087b056e58a" #20 [26371] prio=5 os_prio=31 cpu=448.64ms elapsed=117775.53s tid=0x0000000133010600 nid=26371 waiting on condition [0x0000000172442000]
java.lang.Thread.State: TIMED_WAITING (parking)
at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
- parking to wait for <0x00000007c0877188> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos([email protected]/Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos([email protected]/Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take([email protected]/Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.getTask([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run([email protected]/Unknown Source)
at java.lang.Thread.runWith([email protected]/Unknown Source)
at java.lang.Thread.run([email protected]/Unknown Source)
"Refresh Thread: Equinox Container: c012ab8d-a389-43fb-bf67-3087b056e58a" #22 [25859] daemon prio=5 os_prio=31 cpu=14.35ms elapsed=117775.45s tid=0x000000013290a000 nid=25859 in Object.wait() [0x000000017264e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <0x00000007c082fd58> (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextEvent(EventManager.java:396)
- locked <0x00000007c082fd58> (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:337)
"Framework Event Dispatcher: Equinox Container: c012ab8d-a389-43fb-bf67-3087b056e58a" #23 [25347] daemon prio=5 os_prio=31 cpu=1.15ms elapsed=117775.43s tid=0x000000013290a800 nid=25347 in Object.wait() [0x000000017285a000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextEvent(EventManager.java:396)
- locked <0x00000007c06f7988> (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:337)
"Start Level: Equinox Container: c012ab8d-a389-43fb-bf67-3087b056e58a" #24 [35843] daemon prio=5 os_prio=31 cpu=1126.54ms elapsed=117775.42s tid=0x0000000106049c00 nid=35843 in Object.wait() [0x0000000172a66000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextEvent(EventManager.java:396)
- locked <0x00000007c0830220> (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:337)
"Bundle File Closer" #27 [48403] daemon prio=5 os_prio=31 cpu=9.38ms elapsed=117775.19s tid=0x00000001330c6200 nid=48403 in Object.wait() [0x0000000172f0a000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextEvent(EventManager.java:396)
- locked <0x00000007c03e8800> (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:337)
"SCR Component Actor" #29 [49155] daemon prio=5 os_prio=31 cpu=0.46ms elapsed=117775.06s tid=0x0000000121214800 nid=49155 in Object.wait() [0x0000000173322000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at org.apache.felix.scr.impl.ComponentActorThread.run(ComponentActorThread.java:83)
- locked <0x00000007c0af3798> (a java.util.LinkedList)
at java.lang.Thread.runWith([email protected]/Unknown Source)
at java.lang.Thread.run([email protected]/Unknown Source)
"Worker-JM" #34 [58371] prio=5 os_prio=31 cpu=0.14ms elapsed=117774.55s tid=0x0000000132ab6e00 nid=58371 in Object.wait() [0x0000000173b52000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.core.internal.jobs.InternalWorker.run(InternalWorker.java:61)
- locked <0x00000007c0b60408> (a java.util.ArrayList)
"Java indexing" #37 [57355] daemon prio=5 os_prio=31 cpu=8859.15ms elapsed=117773.29s tid=0x0000000132b36a00 nid=57355 in Object.wait() [0x0000000174176000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:502)
- locked <0x00000007c0ce7b28> (a org.eclipse.jdt.internal.core.search.indexing.IndexManager)
at org.eclipse.jdt.internal.core.search.processing.JobManager$$Lambda/0x000000c8012f5958.run(Unknown Source)
at java.lang.Thread.runWith([email protected]/Unknown Source)
at java.lang.Thread.run([email protected]/Unknown Source)
"pool-2-thread-1" #40 [56579] prio=5 os_prio=31 cpu=2422.32ms elapsed=117773.04s tid=0x0000000132099800 nid=56579 waiting on condition [0x000000017479a000]
java.lang.Thread.State: TIMED_WAITING (parking)
at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
- parking to wait for <0x00000007c1009388> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos([email protected]/Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos([email protected]/Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take([email protected]/Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.getTask([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run([email protected]/Unknown Source)
at java.lang.Thread.runWith([email protected]/Unknown Source)
at java.lang.Thread.run([email protected]/Unknown Source)
"Worker-5" #42 [56067] prio=5 os_prio=31 cpu=597.45ms elapsed=117772.77s tid=0x000000013180d800 nid=56067 in Object.wait() [0x0000000174bb2000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.core.internal.jobs.WorkerPool.sleep(WorkerPool.java:200)
- locked <0x00000007c0a4a3e0> (a org.eclipse.core.internal.jobs.WorkerPool)
at org.eclipse.core.internal.jobs.WorkerPool.startJob(WorkerPool.java:242)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:58)
"WorkspaceEventsHandler" #45 [55299] prio=5 os_prio=31 cpu=60.24ms elapsed=117772.70s tid=0x00000001060ae000 nid=55299 waiting on condition [0x00000001751d6000]
java.lang.Thread.State: WAITING (parking)
at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
- parking to wait for <0x00000007c1450dd0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park([email protected]/Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block([email protected]/Unknown Source)
at java.util.concurrent.ForkJoinPool.unmanagedBlock([email protected]/Unknown Source)
at java.util.concurrent.ForkJoinPool.managedBlock([email protected]/Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await([email protected]/Unknown Source)
at java.util.concurrent.LinkedBlockingQueue.take([email protected]/Unknown Source)
at org.eclipse.jdt.ls.core.internal.handlers.WorkspaceEventsHandler.lambda$0(WorkspaceEventsHandler.java:64)
at org.eclipse.jdt.ls.core.internal.handlers.WorkspaceEventsHandler$$Lambda/0x000000c8013aa5a0.run(Unknown Source)
at java.lang.Thread.runWith([email protected]/Unknown Source)
at java.lang.Thread.run([email protected]/Unknown Source)
"pool-1-thread-1" #46 [53507] prio=5 os_prio=31 cpu=161.67ms elapsed=117772.70s tid=0x0000000133164600 nid=53507 runnable [0x00000001753e2000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.SocketDispatcher.read0([email protected]/Native Method)
at sun.nio.ch.SocketDispatcher.read([email protected]/Unknown Source)
at sun.nio.ch.IOUtil.readIntoNativeBuffer([email protected]/Unknown Source)
at sun.nio.ch.IOUtil.read([email protected]/Unknown Source)
at sun.nio.ch.IOUtil.read([email protected]/Unknown Source)
at sun.nio.ch.SocketChannelImpl.read([email protected]/Unknown Source)
at org.eclipse.jdt.ls.core.internal.ConnectionStreamFactory$NamedPipeInputStream.read(ConnectionStreamFactory.java:173)
at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:82)
at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:114)
at java.util.concurrent.Executors$RunnableAdapter.call([email protected]/Unknown Source)
at java.util.concurrent.FutureTask.run([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker([email protected]/Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run([email protected]/Unknown Source)
at java.lang.Thread.runWith([email protected]/Unknown Source)
at java.lang.Thread.run([email protected]/Unknown Source)
"Attach Listener" #85 [37923] daemon prio=9 os_prio=31 cpu=1472.46ms elapsed=117758.26s tid=0x00000001318b8800 nid=37923 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Worker-8" #97 [65627] prio=5 os_prio=31 cpu=418.35ms elapsed=104428.72s tid=0x000000012125a000 nid=65627 in Object.wait() [0x0000000173d5e000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait0([email protected]/Native Method)
- waiting on <no object reference available>
at java.lang.Object.wait([email protected]/Unknown Source)
at org.eclipse.core.internal.jobs.WorkerPool.sleep(WorkerPool.java:200)
- locked <0x00000007c0a4a3e0> (a org.eclipse.core.internal.jobs.WorkerPool)
at org.eclipse.core.internal.jobs.WorkerPool.startJob(WorkerPool.java:242)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:58)
"GC Thread#8" os_prio=31 cpu=722.08ms elapsed=117774.60s tid=0x00000001307bd690 nid=58627 runnable
"GC Thread#7" os_prio=31 cpu=739.67ms elapsed=117774.60s tid=0x00000001307bcd10 nid=49923 runnable
"GC Thread#6" os_prio=31 cpu=766.26ms elapsed=117774.60s tid=0x00000001312bcbc0 nid=59395 runnable
"GC Thread#5" os_prio=31 cpu=758.01ms elapsed=117775.07s tid=0x00000001206b5ee0 nid=48659 runnable
"GC Thread#4" os_prio=31 cpu=744.87ms elapsed=117775.37s tid=0x000000012069bec0 nid=61723 runnable
"GC Thread#3" os_prio=31 cpu=743.82ms elapsed=117775.74s tid=0x0000000110616f30 nid=27395 runnable
"GC Thread#2" os_prio=31 cpu=745.16ms elapsed=117775.75s tid=0x0000000120758690 nid=27907 runnable
"GC Thread#1" os_prio=31 cpu=774.01ms elapsed=117775.78s tid=0x000000012065cde0 nid=28167 runnable
"VM Thread" os_prio=31 cpu=2480.07ms elapsed=117776.71s tid=0x0000000120607840 nid=21507 runnable
"VM Periodic Task Thread" os_prio=31 cpu=53472.74ms elapsed=117776.72s tid=0x000000013120c6c0 nid=13827 waiting on condition
"GC Thread#0" os_prio=31 cpu=761.69ms elapsed=117776.79s tid=0x00000001306049e0 nid=14595 runnable
JNI global refs: 21, weak refs: 0
jmap 查看堆转信息
用于生成堆转内存快照、内存使用情况
jmap -heap pid
显示Java堆的信息
jmap -dump:format=b,file=heap.hprof pid
format=b
表示以hprof二进制格式转储Java堆的内存
file=<filename>
用于指定快照dump文件的文件名JDK9+:
jhsdb jmap --heap --pid 2168
jstat 显示垃圾回收信息、类加载信息、新生代统计信息

可视化工具
jconsole 对 JVM 的内存、线程、类的监控
用于对JVM的内存、线程、类进行监控,这是一个基于JMX的GUI性能监控工具
打开方式:在Java安装目录的bin目录下直接启动jconsole.exe即可
VisualVM 监控线程、内存情况
能够监控线程、内存情况,查看方法的CPU时间和内存中的对象,已回收的对象,反向查看分配的堆栈
打开方式:在Java安装目录的bin目录下直接启动jvisualvm.exe即可
Java 内存泄露的排查思路
可能泄露的情况:
主要是堆内存泄露
- 获取堆内存快照
dump
文件
通过 jmap 指定打印他的内存快照 dump (Dump 文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到 dump 文件中)
使用 jmap 命令获取运行中程序的 dump 文件
-
jmap -dump:format=b,file=heap.hprof pid
format=b
表示以 hprof 二进制格式转储 Java 堆的内存-
file=<filename>
用于指定快照 dump 文件的文件名
-
使用 vm 参数获取 dump 文件
- 有的情况是内存溢出之后程序则会直接中断,而 jmap 只能打印在运行中的程序,所以建议通过参数的方式的生成 dump 文件
-
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/app/dumps/
使用
VisualVM
分析dump
文件通过审视堆内存信息,定位内存溢出问题
CPU 飙高的排查思路
找到进程中的哪一个线程
ps H -eo pid,tid, %cpu | grep 进程 id
报错
top -H -p 2634223
说明:
- -H 表示显示线程(Thread)
- -p <pid> 表示针对指定进程

ps -eLo pid,tid,pcpu,pmem,cmd --sort=-pcpu | grep 2634223
说明:
- -eL:列出所有线程
- -o:自定义输出格式(pid、tid、cpu使用率、内存、命令)
- --sort=-pcpu:按 CPU 使用率排序
ps -T -p 2634223
这会列出该进程的所有线程(PID 和 TID),兼容性也很好