家具 东莞网站建设,网站开发怎么报价,高清装饰画图片素材网,关键词排名优化品牌准备
事先启动一个web应用程序#xff0c;用jps查看进程id#xff0c;接着用各种jdk自带的命令优化应用
Jmap
jmap -histo 6160 #查看历史生成的实例
jmap -histo:live 6160 #查看当前存活的实例#xff0c;执行过程中可能会触发一次full gc
jmap -histo:live 6160 用jps查看进程id接着用各种jdk自带的命令优化应用
Jmap
jmap -histo 6160 #查看历史生成的实例
jmap -histo:live 6160 #查看当前存活的实例执行过程中可能会触发一次full gc
jmap -histo:live 6160 Downloads/log.txt # 日志导出
打开log.txt查看日志 , 文件内容 num序号instances实例数量bytes占用空间大小class name类名称[C is a char[][S is a short[][I is a int[][B is a byte[][[I is a int[][]
堆信息 堆内存Dump
jmap -dump:formatb,fileeureka.hprof 6160 也可以设置内存溢出自动导出dump文件(内存很大的时候可能会导不出来)
-XX:HeapDumpOnOutOfMemoryError-XX:HeapDumpPath./ 路径
示例代码
public class OOMTest {public static ListObject list new ArrayList();// JVM设置 // -Xms10M -Xmx10M -XX:PrintGCDetails -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/jvm.dump public static void main(String[] args) {ListObject list new ArrayList();int i 0;int j 0;while (true) {list.add(new User(i, UUID.randomUUID().toString()));new User(j--, UUID.randomUUID().toString());}}
}
可以用jvisualvm命令工具导入该dump文件分析 Jstack
用jstack加进程id查找死锁见如下示例
public class DeadLockTest {private static Object lock1 new Object();private static Object lock2 new Object();public static void main(String[] args) {new Thread(() - {synchronized (lock1) {try {System.out.println(thread1 begin);Thread.sleep(5000);} catch (InterruptedException e) {}synchronized (lock2) {System.out.println(thread1 end);}}}).start();new Thread(() - {synchronized (lock2) {try {System.out.println(thread2 begin);Thread.sleep(5000);} catch (InterruptedException e) {}synchronized (lock1) {System.out.println(thread2 end);}}}).start();System.out.println(main thread end);}
} Thread-1 线程名prio5 优先级5tid0x000000001fa9e000 线程idnid0x2d64 线程对应的本地线程标识nidjava.lang.Thread.State: BLOCKED 线程状态
还可以用jvisualvm自动检测死锁 远程连接jvisualvm
启动普通的jar程序JMX端口配置
java -Dcom.sun.management.jmxremote.port8888 -Djava.rmi.server.hostname192.168.65.60 -Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.management.jmxremote.authenticatefalse -jar microservice-eureka-server.jar
PS
-Dcom.sun.management.jmxremote.port 为远程机器的JMX端口-Djava.rmi.server.hostname 为远程机器IP
tomcat的JMX配置在catalina.sh文件里的最后一个JAVA_OPTS的赋值语句下一行增加如下配置行
JAVA_OPTS$JAVA_OPTS -Dcom.sun.management.jmxremote.port8888 -Djava.rmi.server.hostname192.168.50.60 -Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.management.jmxremote.authenticatefalse
连接时确认下端口是否通畅可以临时关闭下防火墙
systemctl stop firewalld #临时关闭防火墙 jstack找出占用cpu最高的线程堆栈信息
/*** 运行此代码cpu会飙高*/
public class Math {public static final int initData 666;public static User user new User();public int compute() { //一个方法对应一块栈帧内存区域int a 1;int b 2;int c (a b) * 10;return c;}public static void main(String[] args) {Math math new Math();while (true){math.compute();}}
} 1、使用命令top -p 显示你的java进程的内存情况pid是你的java进程号比如6440 2、按H获取每个线程的内存情况 3、找到内存和cpu占用最高的线程tid比如19664
4、转为十六进制得到 0x4cd0此为线程id的十六进制表示
5、执行 jstack 19663|grep -A 10 4cd0得到线程堆栈信息中 4cd0 这个线程所在行的后面10行从堆栈中可以发现导致cpu飙高的调用方法 6、查看对应的堆栈信息找出可能存在问题的代码
Jinfo
查看正在运行的Java应用程序的扩展参数
jinfo -flags pid 查看java系统参数 jinfo -sysprops pid Jstat
jstat命令可以查看堆内存各部分的使用量以及加载类的数量。命令的格式如下
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
注意使用的jdk版本是jdk8
垃圾回收统计
jstat -gc pid 最常用可以评估程序内存使用及GC压力整体情况S0C第一个幸存区的大小单位KBS1C第二个幸存区的大小S0U第一个幸存区的使用大小S1U第二个幸存区的使用大小EC伊甸园区的大小EU伊甸园区的使用大小OC老年代大小OU老年代使用大小MC方法区大小(元空间)MU方法区使用大小CCSC:压缩类空间大小CCSU:压缩类空间使用大小YGC年轻代垃圾回收次数YGCT年轻代垃圾回收消耗时间单位sFGC老年代垃圾回收次数FGCT老年代垃圾回收消耗时间单位sGCT垃圾回收消耗总时间单位s
堆内存统计
jstat -gccapacity pid NGCMN新生代最小容量NGCMX新生代最大容量NGC当前新生代容量S0C第一个幸存区大小S1C第二个幸存区的大小EC伊甸园区的大小OGCMN老年代最小容量OGCMX老年代最大容量OGC当前老年代大小OC:当前老年代大小MCMN:最小元数据容量MCMX最大元数据容量MC当前元数据空间大小CCSMN最小压缩类空间大小CCSMX最大压缩类空间大小CCSC当前压缩类空间大小YGC年轻代gc次数FGC老年代GC次数
新生代垃圾回收统计
jstat -gcnew pid S0C第一个幸存区的大小S1C第二个幸存区的大小S0U第一个幸存区的使用大小S1U第二个幸存区的使用大小TT:对象在新生代存活的次数MTT:对象在新生代存活的最大次数DSS:期望的幸存区大小EC伊甸园区的大小EU伊甸园区的使用大小YGC年轻代垃圾回收次数YGCT年轻代垃圾回收消耗时间
新生代内存统计
jstat -gcnewcapacity pid NGCMN新生代最小容量NGCMX新生代最大容量NGC当前新生代容量S0CMX最大幸存1区大小S0C当前幸存1区大小S1CMX最大幸存2区大小S1C当前幸存2区大小ECMX最大伊甸园区大小EC当前伊甸园区大小YGC年轻代垃圾回收次数FGC老年代回收次数
老年代垃圾回收统计
jstat -gcold pid MC方法区大小MU方法区使用大小CCSC:压缩类空间大小CCSU:压缩类空间使用大小OC老年代大小OU老年代使用大小YGC年轻代垃圾回收次数FGC老年代垃圾回收次数FGCT老年代垃圾回收消耗时间GCT垃圾回收消耗总时间
老年代内存统计 jstat -gcoldcapacity pid OGCMN老年代最小容量OGCMX老年代最大容量OGC当前老年代大小OC老年代大小YGC年轻代垃圾回收次数FGC老年代垃圾回收次数FGCT老年代垃圾回收消耗时间GCT垃圾回收消耗总时间
元数据空间统计 jstat -gcmetacapacity pid MCMN:最小元数据容量MCMX最大元数据容量MC当前元数据空间大小CCSMN最小压缩类空间大小CCSMX最大压缩类空间大小CCSC当前压缩类空间大小YGC年轻代垃圾回收次数FGC老年代垃圾回收次数FGCT老年代垃圾回收消耗时间GCT垃圾回收消耗总时间 监视Java进程的垃圾回收情况
jstat -gcutil pid S0幸存1区当前使用比例S1幸存2区当前使用比例E伊甸园区使用比例O老年代使用比例M元数据区使用比例CCS压缩使用比例YGC年轻代垃圾回收次数FGC老年代垃圾回收次数FGCT老年代垃圾回收消耗时间GCT垃圾回收消耗总时间
JVM运行情况预估
用 jstat gc -pid 命令可以计算出如下一些关键数据有了这些数据就可以采用之前介绍过的优化思路先给自己的系统设置一些初始性的JVM参数比如堆内存大小年轻代大小Eden和Survivor的比例老年代的大小大对象的阈值大龄对象进入老年代的阈值等。 年轻代对象增长的速率
可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令共执行10次)通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象如果系统负载不高可以把频率1秒换成1分钟甚至10分钟来观察整体情况。注意一般系统可能有高峰期和日常期所以需要在不同的时间分别估算不同情况下对象增长速率。
Young GC的触发频率和每次耗时
知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次Young GC的平均耗时可以通过 YGCT/YGC 公式算出根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。
每次Young GC后有多少对象存活和进入老年代
这个因为之前已经大概知道Young GC的频率假设是每5分钟一次那么可以执行命令 jstat -gc pid 300000 10 观察每次结果edensurvivor和老年代使用的变化情况在每次gc后eden区使用一般会大幅减少survivor和老年代都有可能增长这些增长的对象就是每次Young GC后存活的对象同时还可以看出每次Young GC后进去老年代大概多少对象从而可以推算出老年代对象增长速率。
Full GC的触发频率和每次耗时
知道了老年代对象的增长速率就可以推算出Full GC的触发频率了Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。
优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率避免频繁Full GC对JVM性能的影响。
系统频繁Full GC导致系统卡顿是怎么回事
机器配置2核4GJVM内存大小2G系统运行时间7天期间发生的Full GC次数和耗时500多次200多秒期间发生的Young GC次数和耗时1万多次500多秒
大致算下来每天会发生70多次Full GC平均每小时3次每次Full GC在400毫秒左右
每天会发生1000多次Young GC每分钟会发生1次每次Young GC在50毫秒左右。
JVM参数设置如下
-Xms1536M
-Xmx1536M
-Xmn512M
-Xss256K
-XX:SurvivorRatio6
-XX:MetaspaceSize256M
-XX:MaxMetaspaceSize256M
-XX:UseParNewGC
-XX:UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction75
-XX:UseCMSInitiatingOccupancyOnly 大家可以结合对象挪动到老年代那些规则推理下我们这个程序可能存在的一些问题
经过分析感觉可能会由于对象动态年龄判断机制导致full gc较为频繁
为了给大家看效果我模拟了一个示例程序(见课程对应工程代码jvm-full-gc)打印了jstat的结果如下
jstat -gc 13456 2000 10000 对于对象动态年龄判断机制导致的full gc较为频繁可以先试着优化下JVM参数把年轻代适当调大点
-Xms1536M
-Xmx1536M
-Xmn1024M
-Xss256K
-XX:SurvivorRatio6
-XX:MetaspaceSize256M
-XX:MaxMetaspaceSize256M
-XX:UseParNewGC
-XX:UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction92
-XX:UseCMSInitiatingOccupancyOnly 优化完发现没什么变化full gc的次数比minor gc的次数还多了 我们可以推测下full gc比minor gc还多的原因有哪些
元空间不够导致的多余full gc显示调用System.gc()造成多余的full gc这种一般线上尽量通过-XX:DisableExplicitGC参数禁用如果加上了这个JVM启动参数那么代码中调用System.gc()没有任何效果老年代空间分配担保机制
最快速度分析完这些我们推测的原因以及优化后我们发现young gc和full gc依然很频繁了而且看到有大量的对象频繁的被挪动到老年代这种情况我们可以借助jmap命令大概看下是什么对象 查到了有大量User对象产生这个可能是问题所在但不确定还必须找到对应的代码确认如何去找对应的代码了 代码里全文搜索生成User对象的地方(适合只有少数几处地方的情况) 如果生成User对象的地方太多无法定位具体代码我们可以同时分析下占用cpu较高的线 程一般有大量对象不断产生对应的方法代码肯定会被频繁调用占用的cpu必然较高
可以用上面讲过的jstack或jvisualvm来定位cpu使用较高的代码最终定位到的代码如下
import java.util.ArrayList; RestController public class IndexController { RequestMapping(/user/process) public String processUserData() throws InterruptedException { ArrayListUser users queryUsers(); for (User user: users) { //TODO 业务处理 System.out.println(user: user.toString()); } return end;} /** * 模拟批量查询用户场景 * return */ private ArrayListUser queryUsers() {ArrayListUser users new ArrayList(); for (int i 0; i 5000; i) { users.add(new User(i,zhuge)); } return users; }
}
同时java的代码也是需要优化的一次查询出500M的对象出来明显不合适要根据之前说的各种原则尽量优化到合适的值尽量消除这种朝生夕死的对象导致的full gc
内存泄露到底是怎么回事
再给大家讲一种情况一般电商架构可能会使用多级缓存架构就是redis加上JVM级缓存大多数同学可能为了图方便对于JVM级缓存就简单使用一个hashmap于是不断往里面放缓存数据但是很少考虑这个map的容量问题结果这个缓存map越来越大一直占用着老年代的很多空间时间长了就会导致full gc非常频繁这就是一种内存泄漏对于一些老旧数据没有及时清理导致一直占用着宝贵的内存资源时间长了除了导致full gc还有可能导致OOM。
这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决比如ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存。 分析GC日志
待补充
JVM调优之GC日志分析及可视化工具介绍_gc日志在线分析_JermeryBesian的博客-CSDN博客
GC日志分析_每天都要进步一点点的博客-CSDN博客