JVM 占用内存过大的分析

/ JavaSE / 1815浏览

在生产环境中,可能会遇到内存占用率达到接近 99%的情况,这时候,如果很清楚加了xxx功能之后出现了此问题,那么排查问题就在新功能上找原因,但是想知道到底是哪些对象比较大,为什么,就得使用JDK提供的一些分析工具了。

常用命令

  1. jps:查看本机java进程信息
  2. jstack:打印线程的栈信息,制作线程dump文件
  3. jmap:打印内存映射,制作堆dump文件
  4. jhat:内存分析工具
  5. jstat:性能监控工具
  6. jconsole:简易的可视化控制台
  7. jvisualvm:功能强大的控制台

这里是分析内存占用太高,所以以上命令不是全部都涉及到,比如jstack是分析线程的、jvisualvm是控制台等等。

jps

它可以来显示当前所有java进程pid的命令,我们可以通过这个命令来查看到底启动了几个java进程(因为每一个java程序都会独占一个java虚拟机实例),不过jps有个缺点是只能显示当前用户的进程id,要显示其他用户的还只能用linux的ps命令。

比如我本机,输入jps命令,显示所有的java进程,如下:

D:\Java\jdk1.8.0_162\bin>jps
22192 Launcher
6480 RemoteMavenServer
17284 Launcher
19900 Jps
21964 Bootstrap

其中,如果要分析java堆内存,那么对应的是 Bootstrap 的pid。

jps -l

输出应用程序main.class的完整package名或者应用程序jar文件完整路径名:

D:\Java\jdk1.8.0_162\bin>jps -l
22192 org.jetbrains.jps.cmdline.Launcher
6480 org.jetbrains.idea.maven.server.RemoteMavenServer
17284 org.jetbrains.jps.cmdline.Launcher
4520 sun.tools.jps.Jps
21964 org.apache.catalina.startup.Bootstrap

可见,由于我启动了一个tomcat,pid 21964 即tomcat的启动类。

jps -v

输出传递给JVM的参数,可查看参数的配置信息,如图,这是一台服务器的参数数据: alt

jps 失效

如果jps命令失效,而我们又要获取pid,还可以使用以下两种方法:

  1. top | grep java
  2. ps -ef |grep java

jmap

得到pid之后,就可以按pid进行分析了,jmap主要用于打印指定java进程的共享对象内存映射或堆内存细节。

⭐ 堆Dump是反映堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。一般在内存不足,GC异常等情况下,我们会去怀疑内存泄漏,这个时候就会去打印堆Dump。

jmap pid

打印的信息分别为:共享对象的起始地址、映射大小、共享对象路径的全程: alt

jmap -heap pid

查看堆使用情况 alt

jmap -histo pid

查看堆中对象数量和大小,打印的信息分别是:序列号、Class实例的数量、内存的占用、类限定名 alt ⭐ 如果是内部类,类名的开头会加上*,如果加上live子参数的话,如jmap -histo:live pid,这个命名会触发一次FUll GC,只统计存活对象。

jmap -dump

将内存使用的详细情况输出到文件。 命令: alt

然后使用jhat命令查看该文件:jhat -port 4000 文件名 alt

启动后在浏览器中访问http:localhost:4000/ alt

总结: 该命令适用的场景是程序内存不足或者GC频繁,这时候很可能是内存泄漏。通过以上命令查看堆使用情况、大量对象被持续引用等情况。

alt

(配图:第比利斯老城)