JVM监控及分析(01)

一、引入

进入tomcat下的webapps文件夹中,将test1.war上传至该目录下,重启tomcat:

sh startup.sh && tail -f ../logs/catalina.out

这时候tomcat会自动解压war包,文件夹文件如下:

在浏览器中访问该项目jsp文件的地址:

使用JMeter对该地址进行压测,录制http请求:

配置压测并发及时间:

查看聚合报告:

 页面报堆内存溢出错误:

查看日志:

常见的内存溢出:

java.lang.OutOfMemoryError: PermGen space → 持久代内存溢出
java.lang.OutOfMemoryError: Java heap space → 堆内存溢出
java.lang.StackOverflowError → 栈内存溢出

通过:jmap -heap pid可以查看内存状况:

如上图表明内存溢出

二、JVM说明

1、JVM内存管理

1)堆与栈:

栈是运行时的单位,而堆是存储的单位。

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据
堆解决的是数据存储的问题,即数据怎么放、放在哪儿
栈的优势是存取速度比堆要快,仅次于寄存器,栈数据可以共享

在Java中一个线程就会相应有一个线程栈与之对应,不同的线程执行逻辑有所不同,因此需要一个独立的线程栈
堆是所有线程共享的。
栈是运行的单位,存储的信息跟当前线程(或程序)相关的信息,包括局部变量、程序运行状态、方法返回值等等,优势是栈的优势是存取速度比堆要快,仅次于寄存器,栈数据可以共享。缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
堆只负责存储对象信息,堆的优势是可以动态地分配内存大小,但缺点是,由于要在运行时动态分配内存,存取速度较慢。

2)JVM内存图解

①new出来的对象,最开始优先分配进入伊甸园区,伊甸园区满了之后,jvm会触发YGC进行垃圾回收:

  A、寻根判断,哪些对象有引用,哪些对象需要进行GC标识

  B、将伊甸园区的存活对象移动到s0存活区,将伊甸园区的需要GC的对象GC掉

②在执行垃圾回收的过程中,整个Java应用程序的线程是暂停没有工作的,直至垃圾回收接收之后,YGC较为频繁。由此可知jvm在进行YGC的时候,会影响应用系统性能。

③当伊甸园区再次满了的时候:

  A、jvm会再次对伊甸园区的对象进行寻根判断标识,然后将存活的对象移动到s1存活区,将伊甸园区的不需要的对象GC掉;

  B、对s0区中的对象进行寻根判断,存活的移动到s1存活区,将s0中不需要的对象GC掉。

上述A、B步骤就叫做两个存活区大小相等,位置互换。

④当伊甸园区第3次满了的时候:

  A、jvm会再次对伊甸园区的对象进行寻根判断标识,然后将存活的对象移动到s0存活区,将伊甸园去的不需要的对象GC掉;

  B、对s1区中的对象进行寻根判断,存活的移动到s0存活区,将s1中不需要的对象GC掉。大小相等,位置互换。

⑤jvm在进行YGC的时候重复上述①到④步骤。

⑥大对象直接进入老年代(大对象 → JDK默认设置大对象的大小)

⑦长期存活的对象进入老年代(长期存活的对象,默认age=15即进行了15次GC之后,有对象一直存活,那么在下次GC的时候,该对象会进入老年代)

⑧对象动态分配原则:比方说,在存活区里边存在着各年龄的对象,jvm会根据一定的方法判断,当某个年龄的对象之和超过存活区的一半大小,那么jvm会将大于等于该age的对象全部移动到老年代

⑨随着时间的推移,老年代内存空间满了之后会jvm会触发FullGC,FullGC的时候会回收整个堆内存和非堆内存(回收的过程都会进程寻根判断,GC掉没用的对象,意味着各区GC完之后还会有些存活的对象);

随着Java程序的继续运行还会触发FullGC,如果对象回收不彻底,老年代被占用的内存比例会越来越高,当再次要往老年代中放置对象的时候,不能将对象再放入老年代了,这时候jvm就会抛出内存溢出的异常。

老年代占整个堆内存的3/8(官方建议)

在进行jvm性能优化的核心要素:尽量延缓FullGC的间隔时间

FullGC的触发条件:

  ①老年代满了的时候,会触发FullGC

  ②permgen(持久代)满了的时候,也会触发FullGC

  ③代码中显示调用System.gc();Runtime.getRuntime().gc();也会触发FullGC

  ④一些悲观策略RMI(基于tcp/ip通信)框架中,会定时的显示调用System.gc();,会触发FullGC

  ⑤Jmap-dump时,也会触发FullGC

2、JVM内存监控

1)jconsole.exe

jconsole.exe连接服务器tomcat

A、运行本地JDK,/bin目录下的jconsole.exe

B、配置linux服务端的catalina.sh文件,在文件的前边加上配置:

JAVA_OPTS="-Xms256m -Xmx256m -Xss1024K -XX:PermSize=128m -XX:MaxPermSize=128m -Djava.rmi.server.hostname=192.168.20.129 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

重启tomcat,在关闭tomcat服务时,报错:

Using CATALINA_BASE: /usr/local/MyFiles/apache-tomcat-8.5.15
Using CATALINA_HOME: /usr/local/MyFiles/apache-tomcat-8.5.15
Using CATALINA_TMPDIR: /usr/local/MyFiles/apache-tomcat-8.5.15/temp
Using JRE_HOME: /usr/local/MyFiles/jdk1.8.0_131
Using CLASSPATH: /usr/local/MyFiles/apache-tomcat-8.5.15/bin/bootstrap.jar:/usr/local/MyFiles/apache-tomcat-8.5.15/bin/tomcat-juli.jar
Java HotSpot(TM) Client VM warning: ignoring option PermSize=128m; support was removed in 8.0
Java HotSpot(TM) Client VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
错误: 代理抛出异常错误: java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostException: Linz: Linz: 未知的名称或服务

tomcat无法关闭

解决办法:

vi /etc/hosts ,在文件127.0.0.1的末尾加上主机名

重启tomcat

C、使用windows端JDK,bin目录中的jconsole.exe连接服务端:

点击连接

至此使用window端的jconsole.exe连接服务器的tomcat成功

2)jmap

A、jmap -heap pid → 发现是否内存泄漏

B、jmap -histo pid > test.txt → 打印堆内存的快照信息,找内存溢出的原因

可以确定,上图中的cn.test.TestBean是自己写的代码

进入项目目录cd ../webapps/test1,查看代码:

vi init1.jsp

泄漏是过程,溢出是结果,由此可知TestMain.list.add(b);是造成内存溢出的原因

C、jmap -dump:live,format=b,file=lhy0523.bin pid

这时候在/usr/local/MyFiles/apache-tomcat-8.5.15/webapps/test1文件夹中生成了一个lhy0523.bin文件

注意:在用jmap命令dump的时候,需要关闭浏览器访问的http://192.168.20.129:8080/test1/init1.jsp网页,要不dump不下来

D、使用jhat -J-mx512m lhy0523.bin命令对lhy0523.bin文件进行解析:

上图报错,说明内存不过,需要将命令的内存改大些:jhat -J-mx900m lhy0523.bin,如下图:

这时候,就可以在客户端浏览器中,用IP加端口号7000,访问:

在此处由于虚拟机配置的原因,花费较长时间,需要说明的是,在对test1进行压测的时候,要时刻跟踪日志,看到日志报内存溢出,要及时停止压测,以避免C步骤中通过jmap -dump:live,format=b,file=filename.bin pid 命令生成的bin文件过大,而导致在该步骤D中使用jhat -J-mx900m filename.bin这个命令由于内存不够,而导致内存溢出。

3)jstack

作用:将dump线程栈信息(方法调用)

用法:jstack 1773 > test.txt

jstack → 分析线程栈(cpu高、IO高、负载高)执行调用方法、获取资源

 线程栈的状态分析:

   ①查看线程的状态,看有没有block状态,at后边是在执行什么调用的是什么方法

   ②应用程序代码响应时间比较长,cpu、负载不高→分析线程栈中的waiting状态,查看线程在等待什么,等待的资源,就是造成系统性能瓶颈的原因

   ③cpu高→running,查看在获取什么资源信息

   定位uscpu高的原因:查看消耗查看cpu高的进程,再看该进程下的线程,再看占用cpu高的线程执行的方法,这个执行的方法就是造成cpu高的原因

     A、top -H -p pid → 查看进程下的线程,分析哪个线程占用cpu高(消耗cpu高的线程,看TIME+,谁占用时间高,就是消耗cpu最高的线程)

     B、使用jstack命令,查看线程栈,并且重定向到一个文件中,将A中,线程的id,转化为16进制的,在重定向中的文件查找,从而找到方法

  另外一种查看进程下,线程的方法:ps -mp pid -o THREAD,tid,time → 替换掉pid

  linux下,将十进制转化为十六进制:printf "%x " tid → tid为线程id

4)jstat

作用:监听垃圾回收,内存使用的情况

jstat -gcutil pid 3000 20 → 监控垃圾回收情况(YGC的时间、次数、FullGC的频率从而对代码、jvm配置进行优化)

命令监视对应用程序造成的性能影响最小

任何监控工具都会耗费系统性能,所以在进行监控的时候,最好使用命令行工具,对性能干扰最小

------------------------------------------------------------

想为你去蹦极,诉说心中对你的眷念... ...

如欢如殇 授以青春鲜活肢体奔忙 如思如忘 驱以老朽深沉灵魂冥想 始自情热激荡 从未敢终于世事炎凉 无能执手相望 无法去尝试结发同床 无力至心死身僵 一息坚强 ------ 我一直没有放弃,如果你也能看到 修身 修禅
原文地址:https://www.cnblogs.com/lz2lhy/p/6886829.html