JVM 实践¶
JVM 命令行工具¶
以下面这段死锁代码为例:
public class DeadLock {
private static Object lockA = new Object();
private static Object lockB = new Object();
public static class RunnableA implements Runnable {
@Override
public void run() {
while(true)
{
synchronized (lockA) {
System.out.println("threadA has acquired lockA");
synchronized (lockB) {
System.out.println("threadA has acquired lockB");
}
}
}
}
}
public static class RunnableB implements Runnable {
@Override
public void run() {
while(true)
{
synchronized (lockB) {
System.out.println("threadB has acquired lockB");
synchronized (lockA) {
System.out.println("threadB has acquired lockA");
}
}
}
}
}
public static void main(String[] args) {
Thread threadA = new Thread(new RunnableA());
Thread threadB = new Thread(new RunnableB());
threadA.start();
threadB.start();
}
}
jps¶
查看进程状态信息,得到进程号 + 名称
jstack¶
根据给定的进程号查看栈信息,可以看到每个线程执行到了哪一行(14 / 29)
jhsdb jmap¶
查看堆内存信息¶
可以看到使用了哪个垃圾收集器,分代情况,堆内存的使用情况等。堆大小最大默认为内存的 1/4
生成到 dump 文件¶
以二进制格式生成到指定文件中
这个文件需要用某些工具打开
dump 文件是进程或系统在某一给定时间的快照。比如进程崩溃时,甚至是任何时候,都可以通过工具备份出进程快照。
包括线程信息、栈信息、异常信息等
jstat¶
这里 M 表示元数据区(方法区在 J8 后的实现)空间使用使用百分比,CGC表示并发回收次数,CGCT表示并发回收消耗的总时间(秒)
可视化工具¶
jconsole¶
C:\Program Files\Java\jdk-17 目录下有 jconsole.exe(Windows)
VisualVM¶
jdk8 中自带,高版本似乎要去官网下载
参数设置¶
这里讨论 springboot 项目通过 jar 包部署的启动参数设置:
在 linux 系统下直接加参数启动 springboot 项目
nohup:在系统后台不挂断地运行命令,退出终端不会影响程序运行
&:让命令在后台执行,退出终端后仍旧执行命令
-
设置堆空间的大小
一般初始化大小和最大大小设置为相同的值,防止垃圾收集器收缩堆产生额外的时间,一般最大大小为物理内存的 1/4;但是也不能太大,如果发生了 fullgc,会扫描整个堆浪费时间
不指定大小默认为字节
-
虚拟机栈设置
每个线程默认开启 1M 内存,一般设置为 256K 就够用。设置的太大会减少可用的线程数
-
Eden 区和每个 Survivor 区的大小比例
-
年轻代晋升老年代阈值
默认为 15,取值范围在 0 ~ 15
-
设置使用哪种垃圾回收器
内存溢出排查¶
方法区 OOM 的主要原因是类加载的太多了,主要考虑排查堆区的内存溢出:
-
使用 jmap -dump 得到 dump 文件。但是如果程序已经崩溃,则此方法失败。则通过 JVM 参数的方式生成 dump 文件
在 idea 中可以通过 Edit Configurations 设置:
-
使用 VisualVM 来打开 hprof 文件,查看 OOM 问题可能的位置
CPU 飙高排查¶
linux 服务器上,使用 top 命令列出 cpu 使用情况,记录一下进程 pid
随后查找进程中 CPU 利用率过高的线程 tid
快速查看十六进制下的 tid
接下来利用进程 pid jstack 一下,查看各个线程栈(此时 tid 是以十六进制表示的),在其中找到目标线程的日志,可以定位哪一行代码出现问题