一、堆区和非堆区

前言:
如果发生内存溢出,主要是通过内存映像文件,来查看到底是那些类,一直在占有没有释放。

  • 1.1 情况分析:

第一种内存溢出:有可能内存泄漏
第二种内存溢出:也有可能内存分配不足导致的

假若:内存泄漏,我们要找到是那个地方把它泄露出去了,一直占有没有释放。
java中的内存泄漏:指new了一个对象之后,一直不释放,这样的内存

  • 1.2 如何导出内存映像文件?
  • 1.2.1 内存溢出JVM自动导出:

当发生内存溢出的时候让jvm自动导出
设置参数:
#当发生内存溢出的时候,把HeapDump导出

-XX:+HeapDumpOnOutOfMemoryError

#当发生内存溢出的时候,把HeapDump出来的路径

-XX:HeapDumpPath=./
  • 1.2.2 使用jmap命令手动导出:

当发生内存溢出的时候,再去dump就有点晚了,当成程序运行一点时间后,我们用jmap手动导出内存映像文件,来具体分析:
执行命令:

jmap -dump:format=b,file=heap.hprof 3456

格式:jmap -dump:format=b,file=heap.hprof pid
命令行输出:

Dumping heap to C:\Users\Administrator.PC-20180929LWLP\Desktop\heap.hprof ...
Heap dump file created

看到以上提示,说明导出成功!!!

二、MAT分析dump文件

打开dump文件

三、案例演示

框架springboot
在浏览器地址栏输入:​​​https://start.spring.io/​

  • 3.1 生成一个springboot项目
  • jmap+MAT实战内存溢出_java


  • jmap+MAT实战内存溢出_jar_02


  • jmap+MAT实战内存溢出_监控/分析_03

  • 3.2 解压压缩包
  • 3.3 在STS或eclipse或idea导入解压的项目
  • jmap+MAT实战内存溢出_内存溢出_04


  • jmap+MAT实战内存溢出_java_05


  • jmap+MAT实战内存溢出_jar_06


  • jmap+MAT实战内存溢出_监控/分析_07

  • 3.4创建一个MemoryController和User对象

实现原理:
因为controller不被回收,它里面的成员变量也 是不会被被回收的,这样就是导致list里面的对象越来越多,占得内存会越来越大,这样就会把我们这个内存撑爆了。

构造内存溢出

1.定义一个 list

2.通过一个循环不停地往这个list里面添加对象

jmap+MAT实战内存溢出_jar_08


3.5 启动项目,添加参数

jmap+MAT实战内存溢出_监控/分析_09


jmap+MAT实战内存溢出_java_10


jmap+MAT实战内存溢出_java_11


浏览器验证:

​http://localhost:8080/heap​​控制台输出:

Exception in thread “http-nio-12345-exec-3” java.lang.OutOfMemoryError: GC overhead limit exceeded

模拟内存溢出自动导出:

jmap+MAT实战内存溢出_jar_12

-Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./

注:./指项目根目录

控制台输出:

jmap+MAT实战内存溢出_内存溢出_13


java.lang.OutOfMemoryError: GC overhead limit exceeded

Dumping heap to ./\java_pid12468.hprof …

Heap dump file created [44843683 bytes in 0.270 secs]

jmap+MAT实战内存溢出_内存溢出_14


mat分析dump文件:

mat下载地址:

​https://www.eclipse.org/mat/downloads.php​

jmap+MAT实战内存溢出_java_15


jmap+MAT实战内存溢出_监控/分析_16


双击打开

jmap+MAT实战内存溢出_java_17


jmap+MAT实战内存溢出_jar_18


jmap+MAT实战内存溢出_java_19


jmap+MAT实战内存溢出_监控/分析_20


jmap+MAT实战内存溢出_java_21


jmap+MAT实战内存溢出_java_22

很明显在MemoryController中,有一个userList,在userList中User对象很多都没有释放。

CPU 线程 线程分析:

jmap+MAT实战内存溢出_内存溢出_23


jmap+MAT实战内存溢出_java_24


结果已经很明显了,解决方案就是:释放对象User就可以了

模拟使用jmap命令手动导出:

总结:理论上说:这2中都可以用,当内存很大的时候自动导出会导不出来,建议使用jmap手动导出即可

jmap -heap 3456
可以看到那一块具体用了多少

上面是导出内存映像文件,下面来用MAT
分析内存溢出,内存映像文件到底存了什么东西?如何利用内存映像文件来定位内存溢出呢?
​​​https://www.eclipse.org/downloads/download.php​

jstack pid > pid.txt
sz pid.txt
线程状态
top -p 15764 -H
​​​https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot​​​​https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr034.html​

多么痛的领悟-代码优化导致的BUG

nohup java -jar monitor_tuuning-0.0.1-SNAPSHOT.jar &

10.5.6.142:12345/loop
top命令查看cpu负载,load acerage:
cpu负载变大请求就进不来了
解决方案:
jstack pid > pid pid.txt
sz pid.txt :下载命令

top -p pid -H:打印所有线程,筛选出占用cpu比较多的线程,看看占用cpu线程在干什么?
printf “%x” pid :线程pid
命令行pid是十进制的,导出的jstack 文件时十六进制的

10.5.6.142/deadlock
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=10.5.6.120"JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=10.5.6.120"
nohup java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=10.5.6.120 -jar monitor_tuuning-0.0.1-SNAPSHOT.jar &
tail -f nohup.out


jmap手动导出内存映像文件
命令行执行:
jmap -dump:format=b,file=heap.hprof 3456
格式:jmap -dump:format=b,file=heap.hprof pid
[root@localhost ~]# cd /app/nmonlogs/
[root@localhost nmonlogs]# ./nmon
按c查看CPU使用信息,按m查看内存使用信息,按n查看网络使用信息
c
jmap -dump:format=b,file=heap.hprof 31322