jvm 内存消耗分析

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 也会消耗内存,而且会随着项目的运行时间而增长,
可见逢年过节之前,系统重启下,还是有原因的,不再是个心理安慰。

猜你喜欢

转载自blog.csdn.net/wuzhuge1990/article/details/88741222