调优工具
1、jps
-q:只显示Java进程的ID
-m:输出Java进程的ID + main函数所在类的名词 + 传递给main函数的参数
-l:输出Java进程的ID + main函数所在类的全限定名(包名 + 类名)
-v:输出Java进程的ID + main函数所在类的名称 + 传递给JVM的参数
应用:可通过此方式快速查看JVM参数是否设置成功
-V、hostid基本用不到,这里就不做介绍了,感兴趣的同学可以自行百度学习。
源码在哪
\openjdk\jdk\src\share\classes\sun\tools\jps\
纯Java编写的
如何识别Java进程
jps输出的信息全是Java进程的信息,是如何做到的?
Java进程在创建的时候,会生成相应的文件,进程相关的信息会写入该文件中。Windows下默认理解是C:\Users\username\AppData\Local\Temp\hsperfdata_username,Linux下默认路径是/tmp/hsperfdata_username
2、jstate
Hotspot自带的工具,通过该工具可实时了解某个进程的class、compile、gc、memory的相关信息。具体可通过该工具查看哪些信息可通过jstat -options查看
为什么说是实时呢,因为底层实现是mmap,及内存映射文件
jstat输出的这些值从哪来的
PerfData文件
Windows下默认理解是C:\Users\username\AppData\Local\Temp\hsperfdata_username
Linux下默认路径是/tmp/hsperfdata_username
PerfData文件
1、文件创建
取决于两个参数
-XX:-/+UsePerfData
默认是开启的
关闭方式:-XX:-UsePerfData。如果关闭了,就不会创建PerfData文件
-XX:-/+PerfDisableSharedMem(禁用共享内存)
默认是关闭的,即支持内存共享。如果禁用了,依赖于PerfData文件的工具就无法正常工作了
2、文件删除
默认情况下随Java进程的结束而销毁
3、文件更新
-XX:PerfDataSamplingInterval = 50ms
即内存与PerfData文件的数据延迟为50ms
纯Java编写
\openjdk\jdk\src\share\classes\sun\tools\jstat\Jstat.java
3、jinfo
4、jstack
5、jmap
6、jconsole
7、visualVM
8、arthas
Java Agent
1、命令行
2、attach
参考文章:https://www.jianshu.com/p/f5efc53ced5d
实战
1、统计线程数
jstack -l 6972 | grep 'java.lang.Thread.State' | wc -l
2、检测死锁
可使用jstack、jconsle、visualVM
3、CPU占用过高
- 定位到占用CPU最高的进程
- 定位到目前占用CPU最高的线程ID
top -H -p 6290
线程ID由十进制转成十六进制,用Python
- 定位线程
jstack 6290(进程ID)|grep 18a1(线程ID,十六进制) -A 30
1、自己想案例模拟OOM并思考如何调优
1)、方法区
参数调整:-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by smoner on 2020/8/23.
*/
public class MetaspaceFlowTest {
public static void main(final String[] args) {
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MetaspaceFlowTest.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o,args);
}
});
System.out.println("running......");
enhancer.create();
}
}
}
GC日志:
GC日志:
[GC (Metadata GC Threshold) 75278K->3488K(375296K), 0.0051576 secs]
[Full GC (Metadata GC Threshold) 3488K->3334K(216064K), 0.0200447 secs]
[GC (Last ditch collection) 3334K->3334K(216064K), 0.0011828 secs]
[Full GC (Last ditch collection) 3334K->1688K(310272K), 0.0253545 secs]
调优原则:
1、最大、最小设置成一样大
2、程序运行起来后,通过visualVM、arthas查看占用了多少内存,向上调优,预留20%以上的空间
2)、堆
import java.util.ArrayList;
import java.util.List;
/**
* Created by smoner on 2020/8/24.
*/
public class HeapOverFlowTest {
int[] intArray = new int[10];
public static void main(String[] args) {
List<HeapOverFlowTest> objs = new ArrayList<HeapOverFlowTest>();
for (;;){
try {
Thread.sleep(1);
}catch (Exception e){
e.printStackTrace();
}
objs.add(new HeapOverFlowTest());
}
}
}
GC日志:
[GC (Allocation Failure) [PSYoungGen: 1344K->320K(2048K)] 7894K->7118K(9216K), 0.0071516 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[GC类型 (GC原因) [新生代垃圾收集器: gc前新生代的内存使用情况->gc后新生代的内存使用情况(新生代总内存)] gc前堆内存的使用情况->gc后堆内存的使用情况(堆总内存), gc耗时] [Times: gc阶段用户空间耗时 gc阶段内核空间耗时, gc阶段实际耗时]
[Full GC (Ergonomics) [PSYoungGen: 320K->0K(2048K)] [ParOldGen: 6798K->5930K(7168K)] 7118K->5930K(9216K), [Metaspace: 9296K->9233K(1058816K)], 0.6733958 secs] [Times: user=1.76 sys=0.00, real=0.68 secs]
[GC类型 (GC原因) [新生代垃圾收集器: gc前新生代的内存使用情况->gc后新生代的内存使用情况(新生代总内存)] [老年代垃圾收集器: gc前老年代的内存使用情况->gc后老年代的内存使用情况(新生代总内存)] gc前堆内存的使用情况->gc后堆内存的使用情况(堆总内存), [Metaspace: gc前元空间的内存使用情况->gc后元空间的内存使用情况(元空间总内存)], gc耗时] [Times: gc阶段用户空间耗时 gc阶段内核空间耗时, gc阶段实际耗时]
调优原则
1、预留30%以上的空间
2、周期性看日志,重点关注full gc频率
3)、虚拟机栈
public class StackOverFlowTest {
private int val = 0;
private String s = null;
private String s2 = null;
public void test(){
val++;
s = ""+val+"";
s2 = ""+val+""+"-";
test();
}
public static void main(String[] args) {
StackOverFlowTest test = new StackOverFlowTest();
try {
test.test();
}catch (Throwable e){
e.printStackTrace();
System.out.println(test.val);
}
}
}
参数
-Xss300k
栈大小相同,栈深度不同,为什么?
栈上分配,轻量级锁
2、死锁、CPU占用过高问题排查,自己实际一遍
1) 死锁实例
public class LockTest {
public static void main(String[] args) {
System.out.println("main start ;");
ReentrantLock lock = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
Thread thread1 = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start");
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+" sleep");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock2.lock();
lock.unlock();
System.out.println(Thread.currentThread().getName()+" end");
}
};
Thread thread2 = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start");
lock2.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
lock2.unlock();
}
};
System.out.println("sub thread start ;");;
thread1.start();
thread2.start();
System.out.println("main end");
}
}
Java VisualVM
未启动前
启动后:
2)、CPU占用过高
public class CPUHigh {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println("hi");
}
}
},"thread-cpuhigh").start();
}
}
(1)、定位到占用cpu最高的进程
top
(2)、定位到占用cpu最高的线程
top -H -p 进程id
(3)、定位线程
jstack 进程id | grep 线程id (16进制) -A 30
3、Java Agent的两种实现方式自己写DEMO
1)、
2)、
参考:
https://www.cnblogs.com/kokov/p/12120033.html
https://blog.csdn.net/wangzhongshun/article/details/100287986
https://www.jianshu.com/p/f5efc53ced5d