-
这里我们创建一个springboot项目, 然后随便写一个contoller, 在这个controller中定义一个List集合, 然后再初始化的时候让集合中10万个字符串, 然后部署并启动项目(这里为了贴近实战, 我是在centos中启动的项目)
-
进入系统, 使用top命令显示系统中的进程信息, 然后点击H(大写), 以内存排序
这时我们看到, Java程序占用了5.5%的内存
前面VIRT列表示的是java申请了多少内存, RES表示实际使用了多少内存 这里的单位是k
在这里主要是看一下概率情况, 同时拿到java程序的进程编号
-
jdk为我们提供了jmap工具, 这个工具可以帮助我们扫描java进行当前的内存情况
jmap的功能很多, 这里我们主要使用这个工具查看占用内存最多的类型是什么
# 这里稍微解释一下, | 的作用是拼接多个命令, |后面的命令利用前面的命令的执行结果继续执行 jmap -histo:live 2249762 | head -20
num : 表示行号, #instances 表示jvm中总共有这个数量的实例对象 #bytes表示这些实例对象总共占用了多少内存(单位byte)
我们看到, jvm中总共有124240个字符串对象, 显然不太合理
到这里, 我们其实仅仅能知道jvm中出现了大量不正常的字符串对象, 至于这些字符串在什么位置, 什么原因产生的还是一无所知
-
接下来, 我们需要具体看看是哪些字符串占用了内存, 并且找出在引用关系, 看看是哪些对象在使用这些字符串
jmap智能粗略的分析内存问题, 如果想要更细致的分析内存,比如说分析对象之间的引用关系, 可以使用jhat工具
这个工具就需要们先将jvm内存详细信息导出到一个文件中,在使用jhat分析
使用 jmap -dump:live,file=heap.bin 进程编号 命令, 将jvm内存导出到一个heap.bin文件中
# 导出jvm结构 jmap -dump:live,file=heap.bin 2254420
# 使用jhat分析文件, jhat回启动一个服务器, 让我们可以在网页上查看信息, 默认端口是7000 # 如果想要修改端口, 可以使用 -port 端口号的方式修改 jhat -port 8088 heap.bin jhat heap.bin
-
接下来我们就可以在浏览器中查看了 , 这里展示的内存中的所有对象的类型
注意, 默认显示的是不包含jdk原生的类型的, 所以, String类型这里显示不出来, 需要将页面滑动到最下面
All classes including platform: 显示所有的类型,包含java原生的类型
Show all members of the rootset: 显示所有的根节点
Show instance counts for all classes (including platform): 显示所有的类型的实例对象数量,包含jdk原生的类型
Show instance counts for all classes (excluding platform): 显示所有的类型的实例对象数量,不包含jdk原生的类型
Show heap histogram : 显示堆内存的统计信息, 其实这里就是上面 jmap -histo:live 命令展示的结果
Show finalizer summary: 实现即将被回收的对象的信息
Execute Object Query Language (OQL) query: 跳转到oql执行页面, 可以让我们使用类似于sql语句的形式查询对象.
-
点击 Show instance counts for all classes (including platform), 显示所有类型的对象信息
这里我们看到, String类型的对象总共有128002个对象, 我们点击去就可以看到具体的字符串对象信息了. 这里我们看到有许多重复的字符串信息, 大概就是问题所在
选择其中一个对象,点击进去, 着重显示的位置表示是当前对象被那个对象引用着, 这里能看到, 当前字符串被一个Object类型的数组引用着
我们可以通过这个关系, 一层层的往上找, 知道找到们熟悉的对象
我们点击之后, 可以进入到这个数组的详情页面, 这里有一个问题, 因为数组中的元素特别多, 当我们进入到数组的详情页面之后, 他的引用关系是显示在页面的最下面的, 让我们查看起来非常麻烦, 而且,如果数据量过多(>百万), 很有可能回引起页面崩溃.
所以, 这里我们可以使用oql语句直接查询
在主页上有进入oql语句的输入页面
点击查询的结果, 进入到ArrayList集合中, 我们终于看到了一个熟悉的类型: MyController
也就是说, 这个包含大量字符串对象的数组, 其实MyController中的ArrayList集合中的数据, 至此, 我么便可以结合源码, 进一步查询问题的所在了
Java内存占用过高问题分析
于 2022-12-23 13:47:05 首次发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)