基础概念
- 吞吐量:
用户代码时间 / (用户代码执行时间+GC时间)
,科学计算、数据挖掘…吞吐量优先(PS+PO) - 响应时间:GC过程中的STW时间越短,响应时间越快 ,网站GUI、API…响应时间优先(G1、CMS+ParNew、ZGC)
一. 调优的目的
- 根据需求进行JVM规划和预调优
- 优化JVM运行环境:慢、卡
- 解决JVM运行中的各种问题:OOM…
二. 步骤
- 熟悉业务场景:吞吐量、响应时间
- 选择最合适的垃圾回收器组合
垃圾回收器跟内存的关系:
- Serial:几十MB
- PS:几百兆-几个G
- CMS:20G
- G1:上百G
- ZGC:4T-16T
- jdk1.8 default:PS+PO
- 计算内存需求
- 选定CPU(越高越好啊)
- 设定年代大小、升级年龄
- 设定日志参数
-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UserGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
或者一天生成一个日志文件
- 观察日志情况
三. 热点问题
1. 系统CPU经常100%,如何查找问题?
一定是有线程占用资源(工作线程或者GC线程)
- 找出哪个进程CPU占用高:
top
- 找出该进程中哪个线程CPU占用高:
top -Hp
- 导出该线程的堆栈:
jstack
- 查找哪个方法(栈帧)消耗时间:
stack
2. 系统内存高,如何查找问题?
- 导出堆内存:
jmap
- 分析:
jhat jvisualvm mat jprofiler
四. 实验
1. 实验环境
CentOS6,JDK1.8,内存4G
2. 测试代码
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class T15_FullGC_Problem01 {
private static class CardInfo {
BigDecimal price = new BigDecimal(0.0);
String name = "张三";
int age = 5;
Date birthdate = new Date();
public void m() {}
}
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
new ThreadPoolExecutor.DiscardOldestPolicy());
public static void main(String[] args) throws Exception {
executor.setMaximumPoolSize(50);
for (;;){
modelFit();
Thread.sleep(100);
}
}
private static void modelFit(){
List<CardInfo> taskList = getAllCardInfo();
taskList.forEach(info -> {
// do something
executor.scheduleWithFixedDelay(() -> {
//do sth with info
info.m();
}, 2, 3, TimeUnit.SECONDS);
});
}
private static List<CardInfo> getAllCardInfo(){
List<CardInfo> taskList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
CardInfo ci = new CardInfo();
taskList.add(ci);
}
return taskList;
}
}
3. 测试过程
先用javac编译然后执行
javac T15_FullGC_Problem01.java
java -Xms200M -Xmx200M -XX:+PrintGC T15_FullGC_Problem01
运行一段时间后发现FullGC频繁,一定是产生了垃圾回收不掉
使用top
查看占用CPU最高的进程ID为2532
使用top -Hp 2532
查看占用CPU最高的线程ID为2534
使用jstack
查看进程dump的信息,关注WAITING BLOCK
这样的字段(配合grep
使用更舒服),看看有没有多个线程在等一把锁,如果有,可能是死锁了,这里并没有发现死锁
使用jmap -histo
查看占用堆内存最多的对象,发现CardInfo
对象非常滴多!
- 如果是线上系统,谨慎使用jmap!会对程序的影响很大!
- jmap -dump:format=b,file=xxx pid 设定了参数HeapDump,OOM的时候会自动产生堆转储文件
- 或者使用在线定位
找到代码的问题是cardinfo对象太多了,想想办法解决一下
其他常用指令/工具
jinfo
:列出jvm的一些状态jstat
:查看GC的一些信息,可以设置interval- JConsole:在远程连接服务器,可以通过窗口调试
自行了解 类似的还有jvisualvm、jprofiler等 - Arthars:Alibaba开源的Java诊断工具
https://arthas.aliyun.com/doc/