JVM规范定义的内存结构
java8已经没有了方法区这个概念,改为了metaspace,存储字符串常量池、静态变量。
Java8的内存结果
jps (JVM process status)可以查看Java虚拟机进程执行情况,-l显示具体的启动类,-m显示具体的入参。-v显示启动配置的jvm参数
jps -l
jstats(JVM statistic Monitoring tool):查看虚拟机上各项运行状态信息。
查看虚拟机号为5640的gc情况。
jstat -gc 5640 100
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
2560.0 2560.0 0.0 0.0 15360.0 3696.2 40960.0 0.0 4480.0 774.9 384.0 75.9 0 0.000 0 0.000 0.000
S0C S1C S0U S1U survivor0和survivor1
EC EU 新生代 总量和使用
OC OU 老生代 总量和使用
MC MU metaspace 总量和使用
CCSC CCSU 压缩类总量和使用
YGC YGCT younggc的次数和时间
FGC FGCT Full的次数和时间
GCT 总的gc时间
jinfo:查看和调整虚拟机的各项参数
jinfo -flag
如查看是否使用了UseSerial垃圾回收器
jinfo -flag UseSerialGC 5640
使用的减号,那么没用用UseSerial收集器。
查看所有的jvm参数
jinfo -flags 7395
Attaching to process ID 7395, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.101-b13
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=262144000 -XX:MaxHeapSize=4164943872 -XX:MaxNewSize=1388314624 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=87031808 -XX:OldSize=175112192 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
Command line: -Dspring.config.location=application-pro.yml -Dspring.profiles.active=pro
jmap(JVM Memory Map For Java):生成堆内存快照,
jmap -dump:format=b,file=jmapDumpFile 3137
也可以在出现内存溢出后自动生成dump文件。利用jdk目录下\jdk1.8.0_144\bin提供的jvisualvm.exe打开。jvisualvm.exe同时支持在线监控,
定位频繁gc测试代码
@GetMapping("/oom")
public Integer oom(){
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
List<User> userList = new ArrayList<>();
for (int i = 0; i < 1000 * 10000; i++) {
User user = new User();
user.setId(i);
user.setUserName("u" + i);
user.setPassword("123");
user.setCreateTime(new Date());
user.setExp1("");
user.setExp2("");
user.setExp3("");
user.setExp4("");
user.setExp5("");
user.setExp6("");
user.setExp7("");
user.setExp8("");
user.setExp9("");
user.setExp10("");
user.setExp11("");
user.setExp12("");
user.setExp13("");
user.setExp14("");
user.setExp15("");
user.setExp16("");
user.setExp17("");
user.setExp18("");
user.setExp19("");
user.setExp20("");
userList.add(user);
}
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.setName("测试线程");
thread.start();
return 1;
}
调整jvm执行时的最大内存
-Xmx1024m
执行如上的耗时代码,在jvisualvm.exe可看到如下图选。
普通不耗时的请求也无法处理。
Exception in thread "测试线程" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at com.test.controller.UserController$3.run(UserController.java:76)
at java.lang.Thread.run(Thread.java:748)
2021-01-18 17:04:49.049 WARN 5956 --- [MI TCP Accept-0] sun.rmi.transport.tcp : RMI TCP Accept-0: accept loop for ServerSocket[addr=0.0.0.0/0.0.0.0,localport=54683] throws
java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.net.NetworkInterface.getAll(Native Method) ~[na:1.8.0_131]
at java.net.NetworkInterface.getNetworkInterfaces(NetworkInterface.java:343) ~[na:1.8.0_131]
at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:86) ~[na:1.8.0_131]
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:400) [na:1.8.0_131]
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:372) [na:1.8.0_131]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
点击jvisualvm.exe生成dump文件。使用momery analyzer分析
显示一个线程生成了太多本地变量,查看stacktrace信息
可以查看到具体的堆栈信息。
jhat(JVM Heap Analysis Tool)分析jmap生成的堆内存快照。
jhat jmapDumpFile
显示Server is ready后在浏览器中输入http://192.168.209.101:7000查看结果
一帮查询堆内存中总容量最大的对象。
jstack:生成线程执行的栈信息,定位线程出现长时间停顿的原因
jstack -l 3137
生成线程dump文件,用jvisualvm.exe打开。
jstack -l 3137 >> thread.tdump
JDK目录\jdk1.8.0_144\bin下提供了图形化的界面显示内存、线程执行情况的工具jconsole.exe,进入之前选择对应的虚拟机即可。
如果出现空循环的问题,可通过堆栈查看出来
测试代码:
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
while (true){
}
}
},"testBusyThread");
thread.start();
Thread.sleep(1000000);
}
定位线上CPU使用率过高的问题。
模拟该场景,在main函数中写个死循环,使用top命令得到该进程id。
使用jps -l查询得到是那个项目
如下命令得到占用的线程
ps -mp 2050 -o THREAD,tid,time
time为占用的时间,tid为线程id
-m显示所有的线程
-p pid进程使用cpu的时间
-o 使用用户自定义的格式。
或者用如下命令
top -Hp 17583
上图中的pid即为线程id。
占用的线程为2051,需要转换为16进制。
printf "%x\n" 2051
得到803,如果出现16进制数为字母,需要转为小写。
如下命令得到具体那个线程
jstack 2050 |grep 803 -A60
整体流程为:
1
通过
t
o
p
得到占用过高的进程
i
d
\color{#FF0000}{1 通过top得到占用过高的进程id}
1通过top得到占用过高的进程id
2
通过
p
s
−
m
p
得到占用过高的线程
i
d
\color{#FF0000}{2 通过ps -mp得到占用过高的线程id}
2通过ps−mp得到占用过高的线程id
3
通过
j
s
t
a
c
k
进程
i
d
得到线程快照,从快照中
g
r
e
p
那个线程
i
d
,即可得到具体是那个线程执行的那个代码块
\color{#FF0000}{3 通过jstack 进程id得到线程快照,从快照中grep那个线程id,即可得到具体是那个线程执行的那个代码块}
3通过jstack进程id得到线程快照,从快照中grep那个线程id,即可得到具体是那个线程执行的那个代码块
部分摘自某课网