文章
问答
冒泡
jvm 内存消耗分析

一: 背景


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和out of memory会伴随你。


即使是个超大型的项目,class,code也不会占用你太多的内存.


线程是独占的


多线程不仅有上下文切换的场景,还有内存的消耗,这是个不定的因素。


GC 也是需要内存的

在NMT中可以看到,GC 也会消耗内存,而且会随着项目的运行时间而增长,

可见逢年过节之前,系统重启下,还是有原因的,不再是个心理安慰。


关于作者

ylzyqt
获得点赞
文章被阅读