Java基础之JVM基础调优与常见问题
常见命令
以下命令的介绍,全部在jdk8环境下运行的;
jps ☆☆☆☆☆
查看当前运行的进程号;
jmap ☆☆☆
jmap命令可以查看jvm的内存信息,class对应的实例个数以及占用的内存大小
jmap -histo
查看当前java进程
[rd@VM-8-12-centos ~]$ jps
8368 Jps
20073 CpuTopThreadHelper
# 查看历史生成的实例
jmp -histo:live 20073
# 查看当前进程存活的实例,其中执行过程中,可能会触发一次fullgc
jmap -histo:live 20073
列头说明:
- num:序号
- instantces:实例数
- bytes:占用空间大小
- class name:类名称
- [C:char[]
- [S:short[]
- [I:int[]
- [B:byte[]
- [[I:int[][]
jmap -heap ☆☆☆☆
打印堆信息
[rd@VM-8-12-centos ~]$ jmap -heap 20073
Attaching to process ID 20073, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09
using thread-local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 968884224 (924.0MB)
NewSize = 19922944 (19.0MB)
MaxNewSize = 322961408 (308.0MB)
OldSize = 40894464 (39.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 15728640 (15.0MB)
used = 0 (0.0MB)
free = 15728640 (15.0MB)
0.0% used
From Space:
capacity = 2097152 (2.0MB)
used = 0 (0.0MB)
free = 2097152 (2.0MB)
0.0% used
To Space:
capacity = 2097152 (2.0MB)
used = 0 (0.0MB)
free = 2097152 (2.0MB)
0.0% used
PS Old Generation
capacity = 16252928 (15.5MB)
used = 260944 (0.2488555908203125MB)
free = 15991984 (15.251144409179688MB)
1.6055199407762097% used
694 interned Strings occupying 46296 bytes.
jmap -dump
导出堆内存快照,用于分析执行dump操作时进程运行状态;
jmap -dump:format=b,file=/home/rd/20073.hprof 20073
进行启动时,可以设置在内存溢出时,自动dump当时内存快照(堆内存很大时,可能会dump失败),具体参数如下:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./xxxx.hprof
jstack ☆☆☆☆
查看当前进程下线程的运行情况,可以用来判断线程占用cpu情况,以及线程锁占用情况;
-- jstack pid
jstack 3305
部分字段说明:
- prio:jvm线程优先级
- os_prio:操作系统线程优化级
- tid:jvm线程id
- nid:操作系统线程id(十六进制)
jinfo ☆☆
查看正在运行的java应用程序的扩展参数;
查看jvm的参数:
[rd@VM-8-12-centos project]$ jinfo -flags 23852
Attaching to process ID 23852, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09
Non-default VM flags:
-XX:CICompilerCount=2
-XX:InitialHeapSize=60817408
-XX:MaxHeapSize=968884224
-XX:MaxNewSize=322961408
-XX:MinHeapDeltaBytes=524288
-XX:NewSize=19922944
-XX:OldSize=40894464
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseParallelGC
Command line:
[rd@VM-8-12-centos project]$
查看java系统参数
[rd@VM-8-12-centos project]$ jinfo -flags 23852
Attaching to process ID 23852, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=60817408 -XX:MaxHeapSize=968884224 -XX:MaxNewSize=322961408 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=19922944 -XX:OldSize=40894464 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
Command line:
[rd@VM-8-12-centos project]$ ^C
[rd@VM-8-12-centos project]$ jinfo -sysprops 23852
Attaching to process ID 23852, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.361-b09
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.361-b09
sun.boot.library.path = /usr/local/java/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = US
user.dir = /home/rd/project
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_361-b09
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/local/java/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator =
java.vm.specification.vendor = Oracle Corporation
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 3.10.0-1160.71.1.el7.x86_64
user.home = /home/rd
user.timezone =
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
user.name = rd
java.class.path = .:/usr/local/java/lib/dt.jar:/usr/local/java/lib/tools.jar
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = com.ddu.jvm.DeadThreadHelper
java.home = /usr/local/java/jre
user.language = en
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_361
java.ext.dirs = /usr/local/java/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path = /usr/local/java/jre/lib/resources.jar:/usr/local/java/jre/lib/rt.jar:/usr/local/java/jre/lib/jsse.jar:/usr/local/java/jre/lib/jce.jar:/usr/local/java/jre/lib/charsets.jar:/usr/local/java/jre/lib/jfr.jar:/usr/local/java/jre/classes
java.vendor = Oracle Corporation
java.specification.maintenance.version = 4
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.cpu.isalist =
jstat ☆☆☆☆☆
实际开发中,经常可以用到的命令,用来查看当前java进行内存使用情况;
jstat [-命令选项][vmid][间隔时间(毫秒)][查询次数]
垃圾回收统计
jstat -gc pid 最常用的命令,可以评估程序内存使用情况以及GC压力的整体情况
[rd@VM-8-12-centos project]$ jstat -gc 23852 5000 10
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
2048.0 2048.0 0.0 0.0 15360.0 4302.9 39936.0 0.0 4480.0 779.8 384.0 76.0 0 0.000 0 0.000 0.000
2048.0 2048.0 0.0 0.0 15360.0 4302.9 39936.0 0.0 4480.0 779.8 384.0 76.0 0 0.000 0 0.000 0.000
2048.0 2048.0 0.0 0.0 15360.0 4302.9 39936.0 0.0 4480.0 779.8 384.0 76.0 0 0.000 0 0.000 0.000
单位统一为:KB
- S0C:新生代第一个Survivor区的大小,单位KB
- S1C:新生代第二个Survivor区的大小
- S0U:新生代第一个幸存区的使用大小
- S1U:新生代第二个幸存区的使用大小
- EC:Eden区的大小
- EU:Eden区的使用大小
- OC:老年代的大小
- OU:老年代的使用大小
- MC:方法区(元空间)大小
- MU:方法区(元空间)使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- YGC:年轻代垃圾回收次数(从当前进程启动开始至当前时间发生的YGC)
- YGCT:年轻代垃圾回收消耗时间,单位s(从当前进程启动开始至当前时间发生的YGCT)
- FGC:老年代垃圾回收次数(从当前进程启动开始至当前时间发生的FYGC)
- FGCT:老年代垃圾回收消耗时间,单位s(从当前进程启动开始至当前时间发生的FYGCT)
- GCT:垃圾回收总消耗时间,单位s(从当前进程启动开始至当前时间发生的GCT)
堆内存统计
[rd@VM-8-12-centos project]$ jstat -gccapacity 23852
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
19456.0 315392.0 19456.0 2048.0 2048.0 15360.0 39936.0 630784.0 39936.0 39936.0 0.0 1056768.0 4480.0 0.0 1048576.0 384.0 0 0
[rd@VM-8-12-centos project]$
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0C:第一个Survivor大小
- S1C:第二个Survivor的大小
- EC:Eden区的大小
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:当前老年代大小
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代gc次数
- FGC:老年代GC次数
新生代垃圾回收统计
[rd@VM-8-12-centos project]$ jstat -gcnew 23852
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
2048.0 2048.0 0.0 0.0 15 15 0.0 15360.0 4302.9 0 0.000
- S0C:第一个Survivor的大小
- S1C:第二个Survivor的大小
- S0U:第一个Survivor的使用大小
- S1U:第二个Survivor的使用大小
- TT:对象在新生代存活的次数
- MTT:对象在新生代存活的最大次数
- DSS:期望的幸存区大小
- EC:Eden区的大小
- EU:Eden区的使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间
新生代内存统计
[rd@VM-8-12-centos project]$ jstat -gcnewcapacity 23852
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
19456.0 315392.0 19456.0 104960.0 2048.0 104960.0 2048.0 314368.0 15360.0 0 0
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0CMX:第一个Survivor区最大大小
- S0C:第一个Survivor区当前大小
- S1CMX:第二个Survivor区最大大小
- S1C:第二个Survivor区当前大小
- ECMX:最大Eden区大小
- EC:当前Eden区大小
- YGC:年轻代垃圾回收次数
- FGC:老年代回收次数
老年代垃圾回收统计
[rd@VM-8-12-centos project]$ jstat -gcold 23852
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
4480.0 779.8 384.0 76.0 39936.0 0.0 0 0 0.000 0.000
- MC:方法区大小
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- OC:老年代大小
- OU:老年代使用大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
老年代内存统计
[rd@VM-8-12-centos project]$ jstat -gcoldcapacity 23852
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
39936.0 630784.0 39936.0 39936.0 0 0 0.000 0.000
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:老年代大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
元数据空间统计
[rd@VM-8-12-centos project]$ jstat -gcmetacapacity 23852
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT
0.0 1056768.0 4480.0 0.0 1048576.0 384.0 0 0 0.000 0.000
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
[rd@VM-8-12-centos project]$ jstat -gcutil 23852
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 28.01 0.00 17.41 19.80 0 0.000 0 0.000 0.000
- S0:幸存1区当前使用比例
- S1:幸存2区当前使用比例
- E:伊甸园区使用比例
- O:老年代使用比例
- M:元数据区使用比例
- CCS:压缩使用比例
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
常见问题
一 CPU使用率持续99.9%☆☆☆☆☆
实例代码:
package com.ddu.jvm;
/**
* CPU飙高
*
* @author wucj
* @date 2024/04/16
*/
public class CpuTopThreadHelper {
public static void main(String[] args) {
run();
}
private static void run() {
CpuTopThreadHelper helper = new CpuTopThreadHelper();
while (true) {
helper.sum();
}
}
private static int sum() {
int a = 100;
int b = 200;
int c = (a + b) * 325;
return c;
}
}
1.1 找到当前机器上运行的java进程号
1.2 查看当前进程下线程占用cpu情况
top命令查看当前进程下线程占用cpu情况
top -p 3305
1.3 查看当前进程下线程的cpu占用情况
在上述步骤(top -p pid)基础上按大写H,查看当前进程下线程的cpu占用情况
从上述打印结果看,当前进程(pid=3305)下操作系统线程号=3306的线程,占用cpu 99.9%;
因为jstack中打印的操作系统线程号为十六进制的,因此需要将操作系统下的线程号转成十六进制;
- 操作系统进程号=3306转成十六进制=cea
1.4 查看当前进程下指定操作系统线程号占用内存情况
通过jstack命令查看当前进程下指定操作系统线程号占用内存情况
jstack 3305 |grep cea -C 10
通过上述打印的日志,可以看到如下代码占用长时间占用cpu
at com.ddu.jvm.CpuTopThreadHelper.run(CpuTopThreadHelper.java:17)
二 排查死锁 ☆☆☆☆☆
源码:
package com.ddu.jvm;
/**
* @date 2024/04/16
*/
public class DeadThreadHelper {
private static Object obj1 = new Object();
private static Object obj2 = new Object();
private static Object obj3 = new Object();
private static Object obj4 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + " locked obj1");
sleep(5);
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + " release obj1");
}
}
}).start();
new Thread(() -> {
synchronized (obj3) {
System.out.println(Thread.currentThread().getName() + " locked obj3");
sleep(5000000);
}
}).start();
new Thread(() -> {
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + " locked obj2");
sleep(5);
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + " release obj2");
}
}
}).start();
new Thread(() -> {
synchronized (obj4) {
System.out.println(Thread.currentThread().getName() + " locked obj4");
sleep(5000000);
}
}).start();
}
private static void sleep(int seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
jstack 23852 > xxxx.info
分析:从上述jstack打印的进程锁占用情况分析,其中Thread-2 Thread-1互相持有对方需要的锁,造成了死锁
线程 | 持有锁-locked | 等待锁-waiting to lock |
Thread-2 | 0x00000000ecc5bed8 | 0x00000000ecc5bec8 |
Thread-0 | 0x00000000ecc5bec8 | 0x00000000ecc5bed8 |
三、内存泄露是如何发生的?
比如日常开发中,我们可能会使用hashmap,不断往里面放缓存数据,但是忘记考虑这个map的容量问题,结果这个缓存map越来越大,持续占用着老年代的很多空间,时间长了就会导致频繁的full gc,这就是一种内存泄漏的场景;
对于一些老旧数据没有及时清理导致一直占用着宝贵的内存资源,时间长了除了导致full gc,还有可能导致OOM。这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如Guava cache、Redis等;
四、case:系统频繁Full GC导致系统卡顿是怎么回事?
首先考虑是否有大对象查询在周期性执行,导致老年代内存周期性被占用,导致Full GC
原文地址:https://blog.csdn.net/weixin_38916435/article/details/137844867
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!