性能调优
定位生产性能问题
生产环境,CPU Memory 告警
- top:找出占CPU比较高的进程${pid}(内存增长,CPU居高不下)
- top -Hp ${pid}:显示所有线程的CPU占比,观察进程中的线程,找出哪个线程CPU和内存占比高
- jps ${pid}定位具体java进程
- jstack 定位线程状况,重点关注:WAITING BLOCKED。 jstack能检查到死锁的存在,并显示哪些线程发生了死锁;
1、是否有死锁:
waiting on <0x0000000088ca3310> (a java.lang.Object)
假如有一个进程中100个线程,很多线程都在waiting on ,一定要找到是哪个线程持有这把锁
怎么找?搜索jstack dump的信息,找 ,看哪个线程持有这把锁RUNNABLE。
2、根据线程编号,用jstack查看线程的调用栈信息,对比看是哪个线程一直在占用CPU。
- 若是VM线程,那就是一直在FGC,若是频繁FGC,需要看是否正常回收(回收不掉?内存泄露)
- 若是业务线程,则查看业务代码问题
- jinfo ${pid} 查看线程信息
- jstat -gc 动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole/jvisualVM/ Jprofiler(最好用)
jstat -gc ${pid} 500 : 每个500个毫秒打印GC的情况.
- jmap - histo 4655 | head -20,jmap主要被用来分析堆内存的工具,查找有多少对象产生
若是压测环境,可以直接用图形界面工具来分析(MAT/jhat/jvisualvm/arthas);
若是生产环境,线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合),则用cmdline arthas 或以下步骤:
- 提前设置好启动参数 -XX:HeapDumpOnOutOfMemoryError 当OOM内存爆了,就生成堆存储文件
- 做负载均衡,把某一台机摘除流量后,再分析(或把流量复制一份到备份机,然后对备份机进行分析)
- 使用TCP copy命令把流量复制并同时打到生产环境和测试环境,在测试环境做性能分析和观察
- 在压测环境分析
- 堆存储文件分析工具:MAT/jhat/jvisualvm
- https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
jhat -J-mx512M xxx.dump
http://192.168.17.11:7000
拉到最后:找到对应链接
可以使用OQL查找特定问题对象
- jmap - histo 4655 | head -20,查找有多少对象产生
工具篇
分析HeapDump文件
jconsole远程连接
-
程序启动加入参数:
java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11111 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false XXX
-
如果遭遇 Local host name unknown:XXX的错误,修改/etc/hosts文件,把XXX加入进去
192.168.17.11 basic localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
-
关闭linux防火墙(实战中应该打开对应端口)
service iptables stop chkconfig iptables off #永久关闭
-
windows上打开 jconsole远程连接 192.168.17.11:11111
jvisualvm远程连接
https://www.cnblogs.com/liugh/p/7620336.html (简单做法)
arthas在线排查工具
- 为什么需要在线排查?
在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因。为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出入参,然后重新打包发布,如果打了日志还是没找到问题,继续加日志,重新打包发布。对于上线流程复杂而且审核比较严的公司,从改代码到上线需要层层的流转,会大大影响问题排查的进度。 - jvm观察jvm信息
- thread定位线程问题
- dashboard 观察系统情况
- heapdump + jhat分析
- jad反编译
动态代理生成类的问题定位
第三方的类(观察代码)
版本问题(确定自己最新提交的版本是不是被使用) - redefine 热替换
目前有些限制条件:只能改方法实现(方法已经运行完成),不能改方法名, 不能改属性
m() -> mm() - sc - search class
- watch - watch method
- 没有包含的功能:jmap
常见问题常见的问题有:
CPU异常高,甚至到100%
https://www.cnblogs.com/python-django-spid/p/16480423.html
思路:top 、top -Hp 查看使用CPU高的进程
进程一般存在7种基础状态:D-不可中断睡眠、R-可执行、S-可中断睡眠、T-暂停态、t-跟踪态、X-死亡态、Z-僵尸态。
D、S:SLEEP态进程不会占用任何CPU资源。
T、t、Z:暂停态、僵尸态 进程会释放所有占用资源。
user(us),代表用户态CPU时间
nice(ni),代表低优先级用户态时间
system(sys),代表内核态CPU时间
idle(id),代表空闲时间,注意不包括等待IO时间
iowait(wa),表示等待IO的CPU时间
irq(hi),代表处理硬中断的CPU时间
softirq(si),代表处理软中断的CPU时间
steal(st),代表当系统运行在虚拟机的时候,被其他虚拟机占用的CPU时间
guest(guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间
guest_nice(gnice),代表以低优先级运行虚拟机的时间
IOwait过高
JVM进程Hang
https://mp.weixin.qq.com/s/7SHlfonUEW8oWx_IAjuYRw
OOM(内存溢出)
内存泄露
频繁GC
…
示例
分析过程:
-
top 找出占CPU高的进程 -->15856
-
top -Hp 找出进程占CPU高的线程
-
jps 列出本地或者远程主机上的所有JVM进程。(jps -lm 显示主类的全限定包名或者jar包的全路径名)
-
jstack 主要JVM进程的线程栈信息:jstack 15856–》查看到没有死锁 WAITING BLOCKED
-
jmap -heap 15856 分代查看对内存使用情况
新生代使用的少,老年代使用90%!
jmap -histo 15856,查看堆内存直方图
jmap -dump:live,format=b,file=helen.hprof 15856,将java heap信息dump成hprof格式的二进制文件,子选项live被指定的时候,仅仅那些存活的堆内存中对象才会被dump出来。被dump出来的文件可以通过__jhat__工具查看
- jhat helen.hprof
执行命令,分析完后会启动7000端口
访问http://localhost:7000/
- Java自带工具jvisualVM.exe,分析hprof文件
package com.vip.fcs.gc;
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;
}
}