一、选择垃圾收集器
垃圾收集器和内存大小有关
一般情况,
serial+serial old 适用几十兆内存
ps+po 适用几百兆~几个G
parNew+CMS 可以用到20G
G1 可以用到上百G
ZGC 可以 4T~16T
1. 常见垃圾收集器组合参数设定
-XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
-XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
-XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
-XX:+UseG1GC = G1
jvm命令行参数:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
二、规划
1. 调优要根据具体的业务场景,不要抛开业务场景
2. 调优需要有监控,根据压测结果,查看具体的结果
三、解决方案
1. top命令
查看当前cpu和内存占用较高的进程
例如,下图中pid为1364的进程cpu和内存的占用都比较高
2. top -Hp pid
观察进程中的线程,哪个线程CPU和内存占比高
top -Hp查看的是10进制的线程号
3. jps
定位具体java进程
4. jstack pid
把该进程的每个线程都列出来(线程状态、nid)
重点关注:WAITING BLOCKED(waiting on <0x0000000088ca3310> (a java.lang.Object) )
如果有一个进程中有很多线程都在waiting on <xx> ,一定要找到是哪个线程持有这把锁
怎么找?搜索jstack dump的信息,找<xx> ,看哪个线程持有这把锁RUNNABLE
其中nid就是16进制的线程号,与top -Hp的线程号对应
5. jinfo pid
查询线程的具体信息
6. jstat -gc
打印垃圾回收信息,后面可以加一个时间参数(毫秒),间隔时间打印垃圾回收信息
7. jmap
jmap -histo pid | head -20:查看内存占用的前20个类
jmap -dump:format=b,file=xxx pid/jmap -histo:手动导出堆转储文件(对系统影响较大)
1) 可以在启动程序的时候设定启动参数:-XX: +HeapDumpOnOutOfMemoryError,这样oom的时候,会自动产生堆转储文件
2)做了服务器的高可用(备份),停掉一台服务器,没有影响
8. 分析dump文件
使用MAT / jhat /jvisualvm 进行dump文件分析
jhat用法: java命令--jhat命令使用 - 白灰 - 博客园
9. jconsole
图形化界面,需要在服务器打开JMX
程序启动参数:
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
windows上打开 jconsole远程连接
10.jvisualVm
和jconsole差不多,都是图形化的界面,但是比jconsole更好用一点
四、在线排查工具arths
1. 背景
在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因。为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出入参,然后重新打包发布,如果打了日志还是没找到问题,继续加日志,重新打包发布。对于上线流程复杂而且审核比较严的公司,从改代码到上线需要层层的流转,会大大影响问题排查的进度。
2. jvm命令观察jvm信息
3. thread定位线程问题
4. dashboard 观察系统情况
5. heapdump + jhat分析
6. jad反编译
动态代理生成类的问题定位
第三方的类(观察代码)
版本问题(确定自己最新提交的版本是不是被使用)
7. redefine 热替换
五、实战
1. 系统CPU经常100%,如何调优?
CPU100%那么一定有线程在占用系统资源
1)找出哪个进程cpu高
top命令,查找cpu占用很高的进程号
top
2)该进程中的哪个线程cpu高(top -Hp)
top -Hp 56959
查看该进程下哪个线程cpu占用高
3)把刚刚的线程号转成16进制
printf "%x\n" 56960
4)导出该线程的堆栈 (jstack)
jstack 56959 | grep de80 -A 30
此时,可以看到,JvmTest的第8行有问题
果然,这个地方有个死循环
2. 系统内存飙高,如何查找问题?(OOM)
1) 导出堆转储文件
可以通过加启动参数,这样出现OOM的时候,会自动导出堆转储文件hprof
-XX:+HeapDumpOnOutOfMemoryError
2)分析
可以通过jvisualVm分析堆转储文件
判断是内存泄漏还是内存溢出,
如果是内存泄漏,找到GC Roots引用链,定位到对象创建的位置,找到产生内存泄漏的代码位置
如果是内存溢出,看看能否调大堆参数(Xms Xmx),再看看是否有代码问题