java项目,内存消耗在了哪里?
一: 背景
java项目,在大家的感官中,都是比较耗内存的,但是具体耗在了哪里,
没有一个明确的文档来阐述,此文档从实验的角度来看具体内存耗在了哪里, 时间有限,排版随意.
二: 环境准备:
1: docker环境
本文中的项目,将通过docker环境来进行发布
2: springboot项目一个
即使不是springboot的项目,一样也可以使用,注意启动命令即可
3: 测试环境机器一台
最好还是有点内存,cpu剩余的
三: 项目配置:
AVA_TOOL_OPTIONS配置为: -Xms256m -Xmx256m -Dspring.profiles.active=test -Dserver.port=12307
四: 现象
- 4.1 : 登录机器,搜索刚刚发布的项目
- 4.2 : 检查docker容器所消耗的资源
- 4.3 : 进入容器,看看到底这个java进程耗了多少内存
五: 问题
- 5.1: 为什么 -Xmx256M ,但是实际上,刚启动1分钟左右的项目,就已经使用了390M左右的内存了?
- 5.2: 这390M 左右的内存,到底消耗在了哪里?
六: dump
dump下来具体的jvm信息,好吧,dump文件一共111M,下载下来耗费了不少的时间
下载在本地为monitor.hprof,使用mat工具打开后如下:
可以看到,堆内存使用了仅仅62.4M, 没有超出我们设置的256M内存的设置,这也就是没有出现OutOfMemory错误的原因,
但是怎么解释那剩下的300M耗在了什么地方?
先来观察下jvm的内存模型: 目前可以看到jvm中不仅仅是堆内存,还有其他的内存区域,怀疑是堆外内存占用的问题
七: java Native Memory tracking
写在前面: 昨天用的是open-jdk8的镜像,今天换成了open-jdk9的镜像,一下就变成了消耗556M
修改启动命令为:
-Xms256m -Xmx256m -XX:NativeMemoryTracking=summary -Dspring.profiles.active=test -Dserver.port=12307
然后执行jcmd命令,提示不支持,查遍资料,原来 -XX:NativeMemoryTracking=summary 不支持在
JAVA_OPTIONS 或者 JAVA_TOOLS_OPTIONS中使用,没有办法,修改Dockfile 为
ENTRYPOINT ["java", "-XX:NativeMemoryTracking=summary" ,"-jar", "/app/bin/app.jar"]
然后在容器中执行jcmd命令:
jcmd 1 VM.native_memory summary
结果如下:
- 1 :堆
Java Heap (reserved=262144KB, committed=262144KB)
(mmap: reserved=262144KB, committed=262144KB)
java堆内存使用了262144 / 1024 = 256 M , 根据之前的dump数据来看,当前的jvm堆内存实际只使用了62.4M,
可见 -Xms256m 参数的威力,不管实际使用多少,只要设置了-Xms,那么当前的堆内存至少使用这么多.
- 2 : 类
Class (reserved=1122020KB, committed=81456KB)
(classes #13462)
(malloc=1764KB #18239)
(mmap: reserved=1120256KB, committed=79692KB)
类,方法区,动态分配内存,内存映射,等等,一共用了 81456 / 1024 = 79M
- 3: 线程
Thread (reserved=90811KB, committed=90811KB)
(thread #89)
(stack: reserved=90420KB, committed=90420KB)
(malloc=287KB #449)
(arena=103KB #176)
根据上线米黄色图,线程是有独立内存的,这里可以看到 实际一共使用了 90811 / 1024 = 88M 万万没想到....
- 4: code
Code (reserved=249558KB, committed=27478KB)
(malloc=1870KB #8533)
(mmap: reserved=247688KB, committed=25608KB)
好吧,code也占用了 27478/1024 = 26M
- 5: GC
GC (reserved=47993KB, committed=47993KB)
(malloc=5429KB #9592)
(mmap: reserved=42564KB, committed=42564KB)
更万万没有想到的是,GC也占用了47993/1024 = 46M
八: 一些启示
- 合理设置堆内存大小
堆内存是jvm中使用的最大的一块空间, -Xms,-Xmx的设置还是很有必要的,这个要根据项目来判断,很明显,
过大的、过小的设置都不合理,过大的浪费空间,过小的话,gc和outofmemory会伴随你。
-
即使是个超大型的项目,class,code也不会占用你太多的内存.
-
线程是独占的
多线程不仅有上下文切换的场景,还有内存的消耗,这是个不定的因素。
- GC 也是需要内存的
在NMT中可以看到,GC 也会消耗内存,而且会随着项目的运行时间而增长,
可见逢年过节之前,系统重启下,还是有原因的,不再是个心理安慰。