JVM调优第一步,了解JVM常用命令行参数
标准: - 开头,所有的HotSpot都支持
非标准:-X 开头,特定版本HotSpot支持特定命令
不稳定:-XX 开头,下个版本可能取消
java -version
:查看java 的版本java -X
:查看java 非标准的、特定HotSpot的特定命令java -XX:+PrintFlagsFinal -version | grep CMS
。(Linux过滤查找)
PrintFlagsFinal
:查看jvm默认参数-XX:+PrintFlagsInitial
是打印所有的默认参数设置-XX:+PrintFlagsFinal
是打印最终值,如果某个默认值被新值覆盖,显示新值-XX:+PrintCommandLineFlags
是打印命令行参数。package com.mashibing.jvm.c5_gc;
//-XX:+PrintGCDetails -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -XX:+PrintVMOptions -
public class T01_HelloGC {
public static void main(String[] args) {
for(int i=0; i<10000; i++) {
byte[] b = new byte[1024 * 1024];
}
}
}
-XX:+PrintGCDetails
:查看JVM详细信息-XX:+UseConcMarkSweepGC
= ParNew + CMS + Serial Old-XX:+PrintFlagsFinal
:最终参数值-XX:+PrintVMOptions
:java -XX:+PrintCommandLineFlags HelloGC
: HelloGC是测试类java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC T01_HelloGC
-Xmn10M
:n是new,新生代的大小-Xms40M
:设置 heap 初始化大小-Xmx60M
:设置 heap 最大值 (与初始值一般设置同大小值)-XX:+PrintCommandLineFlags
:打印命令行参数-XX:+PrintGC
:打印GC回收信息-XX:+PrintGCDetails
:GC详细信息-XX:+PrintGCTimeStamps
:GC时间-XX:+PrintGCCauses
:GC原因
8. java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC
9. java -XX:+PrintFlagsInitial
默认参数值
10. java -XX:+PrintFlagsFinal
最终参数值
11. java -XX:+PrintFlagsFinal | grep xxx
找到对应的参数
* java -XX:+PrintFlagsFinal -version |grep GC
:找与GC相关的命令行参数
内存泄漏memory leak,内存溢出out of memory
问题:
解释:
在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
内存泄漏得多了,就会发现要分配内存的时候没有地方分配了,也就是内存溢出了
每种垃圾回收器的日志格式是不同的!
PS日志格式
// 打印GC的详细信息
java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:PrintGCDetails T01_HelloGC
就是对2.1中的打印的信息,做详细的解刨
heap dump信息解刨如下:
eden space 5632K, 94% used [0x00000000ff980000,0x00000000ffeb3e28,0x00000000fff00000)
后面的内存地址指的是,起始地址,使用空间结束地址,整体空间结束地址
两个概念
吞吐量
:用户代码时间 /(用户代码执行时间 + 垃圾回收时间)响应时间
:STW(stop the world)
越短,响应时间越好所谓调优,首先确定,追求啥?吞吐量优先,还是响应时间优先?
还是在满足一定的响应时间的情况下,要求达到多大的吞吐量
问题:
吞吐量优先
响应时间优先(网站类型)
JVM规划和预调优
从下面三个章节开始细讲:
QPS、TPS、PPS(Per Second)
淘宝20年最多并发 54W
12306号称最多并发,上百万并发。
面试官问:你们服务最大的并发量是多少?
-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log
指定文件名和路径-XX:+UseGCLogFileRotation
:GC文件循环使用-XX:NumberOfGCLogFiles=5
:GC日志文件为5个-XX:GCLogFileSize=20M
:每个GC文件大小为20M-XX:+PrintGCDetails
:打印GC详细信息-XX:+PrintGCDateStamps
:GC时间戳-XX:+PrintGCCause
:GC原因接下来较考虑一个订单产生多少内存。即new出来订单对象,需要多少内存
。假设一个订单对象为512k,1000订单总和是500M左右。初次设定参数后,就可以进行压测,满足不了要求就扩大参数,再不行就加服务器数量。
12306应该是中国并发量最大的秒杀网站:号称并发量100W最高
CDN -> LVS -> NGINX -> 业务系统 -> 每台机器1W并发(10K问题) 100台机器
一般先从CDN开始,在全国做不同的CDN缓存,接下来是一堆的LVS,接下来就是NGINX,接下来就是Tomcat等服务器。
Redis可以撑得住单机1w并发。
普通电商订单 -> 下单 ->订单系统(IO)减库存 ->等待用户付款
此外,架构设计也是和业务逻辑紧密相关的。
大流量的处理方法:分而治之。
有一个50万PV的资料类网站(从磁盘提取文档到内存)原服务器32位,1.5G的堆,用户反馈网站比较缓慢,因此公司决定升级,新的服务器为64位,16G的堆内存,结果用户反馈卡顿十分严重,反而比以前效率更低了。
频繁YGC
,STW(stop the world)长,响应时间变慢
。FGC
时间越长PS+PO
(JDK1.8) 改成 PN + CMS
或者 G1
系统CPU经常100%,如何调优?(面试高频、美团问过)
我之前写过一篇文章,就是解决这个问题的
Java面试题之cpu占用率100%,进行定位和解决
CPU100%那么一定有线程在占用系统资源,
top
)top -Hp
)jstack
)jstack
)jmap
)jhat
jvisualvm
jmat
jprofiler
… )jstat
jvisualvm
jprofiler
arthas
top
无监控,不调优
一个案例理解常用工具
测试代码:
package com.mashibing.jvm.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;
}
}
在jvm路径下的终端中:java -Xms200M -Xmx200M -XX:+PrintGC com.mashibing.jvm.c5_gc.T15_FullGC_Problem01
一般是运维团队首先受到报警信息(CPU Memory)
top
命令观察到问题:内存不断增长 CPU占用率居高不下。找到高内存的pid进程号。
老师讲解的:
我的Mac:内存占了46.7%,在越来越大。
top -Hp pid(进程号)
:打印进程里的所有线程。观察线程,看哪个线程CPU和内存占比高。正常来说占CPU比较多的是垃圾回收的线程比较多,因为垃圾太多回收不过来了,每次只能回收一点点。
jstack
:查看使用方法。可以定位具体的线程,查看问题。注意:jstack 所需的 pid号 是十六进制,通过 top 得到的 进程/线程号 都是十进制的,所以需要十进制转十六进制。
jstack pid(进程)
:会把该进程下的所有线程
都给打印出来。
最开始的线程,这里起了50个线程,所以这里是50开始的,倒序的。
还有一个点,需要看**线程的状态
**。 最后的线程,中间省略啦。
Reference Handler线程
:处理引用,JVM内部的线程。
Finalizer线程
:垃圾回收线程。
在往下都是垃圾回收的线程。
jps
:java 的 ps,打印java 的相关进程
。即定位具体java进程。(win、Linux 都行)
jstack
: 定位线程状况,重点关注:WAITING、BLOCKED
eg.
waiting on <0x0000000088ca3310> (a java.lang.Object)
很重要,意思就是:waiting 正在等待这把锁的释放,
jstack pid(进程)
:会将进程里的所有线程都给列举出来。
假如有一个进程中100个线程,很多线程都在 waiting on
,一定要找到是哪个线程持有这把锁
怎么找?搜索 jstack dump
的信息,找
,看哪个线程持有这把锁RUNNABLE
作业:1:写一个死锁程序,用jstack观察 2 :写一个程序,一个线程持有锁不释放,其他线程等待
为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称?
答:创建线程或者线程池时请指定有意义的线程名称,方便出错时回溯。
public class TimerTaskThread extends Thread {
public TimerTaskThread(){
super.setName("TimerTaskThread");
...
}
}
怎么样自定义线程池里的线程名称?
答:(自定义ThreadFactory)
jstat -gc
:(不好用,可视化不好用)动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole/jvisualVM/ Jprofiler(最好用,但是花钱)
(下面一一讲解了)
jstat -gc 4655 500
: 每个500个毫秒打印GC的情况。
如果面试官问你是怎么定位OOM问题的?如果你回答用图形界面(错误)
因为 上线的项目绝不会使用JMX等线程时时刻刻的监控,太影响项目了。
jmap - histo 4655 | head -20
,查找有多少对象产生
jmap -dump:format=b,file=xxx pid
:jmap命令:https://www.jianshu.com/p/a4ad53179df3
线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合)
1:设定了参数HeapDump,OOM的时候会自动产生堆转储文件
2:很多服务器备份(高可用),停掉这台服务器对其他服务器不影响
3:在线定位(一般小点儿公司用不到)
这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用, 线上系统慎用。
java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.mashibing.jvm.gc.T15_FullGC_Problem01
使用MAT / jhat /jvisualvm 进行dump文件分析
https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
jhat -J-mx512M xxx.dump
http://192.168.17.11:7000
拉到最后:找到对应链接
可以使用OQL查找特定问题对象
Linux没有图形化界面,一般都是win链接Linux;一般远程很少使用图像化界面观察得到, 一般可以使用在线跟踪,阿里的arthas,当然远程监控也有用。
jconsole远程连接时,远程需要开一些服务,JMX(Java Manager Extensions,java管理拓展),就是远程管理、监控一些java进程,需要在服务器上的JMX打开。然后用支持的JMX的工具去连接展示即可
程序启动加入参数(开启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 接下来的参数。。。
全的参数:
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 java -Xms200M -Xmx200M -XX:+PrintGC com.mashibing.jvm.c5_gc.T15_FullGC_Problem01
如果遭遇 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
有限制,不如JMX协议。
https://www.cnblogs.com/liugh/p/7620336.html (简单做法)
mac的路径:/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/bin
我这里以本地具体。页面展示如下: