组件内存分析
1.背景
新体系下服务的内存占用情况逐渐被关注,本文将对组件的内存使用情况做个分析与优化。
2.jvm内存区域
java服务内存主要分为两大类:堆内存和非堆内存。
2.1 堆内存
堆内存是我们通常最熟悉的部分,它是存储由应用程序创建的对象的位置。
堆空间内存默认分配情况:
老年代 : 三分之二的堆空间 新生代 : 三分之一的堆空间 eden区: 8/10 的新生代空间。survivor0 : 1/10 的新生代空间,survivor1 : 1/10 的新生代空间。
2.2 非堆内存
非堆内存分为几个不同的区域:
- Metaspace:由Klass Metaspace和NoKlass Mestaspace组成。Klass Metaspace
就是用来存class文件,NoKlass Metaspace专门来存class相关的其他的内容,比如
method,constant Pool等 - Nio DirectByteBu|er分配的内存:ByteBu|er类提供allocateDirect(int
capacity)进行堆外内存的申请,底层通过unsafe.allocateMemory(size)实现,会
调用malloc方法进行内存分配 - JNI分配的内存:JIT编译时使用本地内存,并且JIT的输入(Java字节码)和输出(可
执行代码)也都是保存在Native Memory - 本地方法栈:JNI调用
- 线程栈分配占用的系统内存:线程所占用内存大小
- jvm本身运行过程分配的内存:GC相关使用到的一些堆外内存
3.内存分析相关指令与工具
java本身提供了多种方法和工具来帮助开发人员查看和分析GC及JVM内存的状况,同时也有一些开源和第三方工具可用于查看、分析GC及JVM内存的状况。通过这些分析,可以排
查程序中内存泄露的问题及调优程序的性能。
3.1 jdk命令
可使用的JDK命令有jps、jstat、jmap、jhat、jstack、jinfo。
https://blog.csdn.net/wangxiaotongfan/article/details/82560739
1. jps
-
jps主要用来输出JVM进程状态信息。
-
语法:
- jps [-q] [-mlvV] [[:]]
- -q 输出进程的 pid
- -m 输出传递给 main.class 方法的参数,实用的一个命令,jps -ml 比较实用的组合,会显示包名/类名/参数
- -l 输出main类或Jar的全限名
- -v 输出传入JVM的参数
- jps [-q] [-mlvV] [[:]]
-
-V 输出通过.hotsportrc或-XX:Flags=指定的jvm参数( 通过文件传递给JVM 的参数 )
2. jstat
- jstat用来监视VM内存内的各种堆和非堆的大小及其内存使用量
- 语法
- jstat -[-t] [-h]
- option: 参数选项 - -class 类加载的行为统计
- -compiler HotSpt JIT编译器行为统计
- -gc 垃圾回收堆的行为统计
- -gccapacity 各个垃圾回收代容量(young,old,meta)和他们相应的空间
统计- -gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
- -gcmetacapacity 统计元空间使用情况
- -gcnew 显示新生代统计信息
- -gcnewcapacity 统计新生代及内存使用情况
- -gcold 统计老年代和元空间使用情况
- -gcoldcapacity 统计老年代内存使用情况
- -gcutil 显示各个各代内存使用百分比
- -printcompilation Hotspot方法编译统计情况
- -t: 可以在打印的列加上Timestamp列,用于显示系统运行的时间
- -h: 可以在周期性数据数据的时候,可以在指定输出多少行以后输出一次表头
- vmid: Virtual Machine ID( 进程的 pid)
- interval: 执行每次的间隔时间,单位为毫秒
- count: 用于指定输出多少次记录,缺省则会一直打印
- -compiler HotSpt JIT编译器行为统计
参考资料: https://blog.csdn.net/cockroach02/article/details/82670500
3.jmap
-
jmap用来查看堆内存使用状况 。
-
语法
- jmap [option] pid
- jmap [option] executable core
- option: 选项参数
- none 查看进程的内存映像信息,类似 Solaris pmap 命令
- -heap 显示Java堆详细信息
- -histo:线下堆中对象的统计信息
- -clstats:Java堆中内存的类加载器的统计信息
- -finalizerinfo:显示在F-Queue队列等待Finlizer线程执行nalizer方法的对象
- -dump:生成堆转储快照
- -F:当-dump没有响应时,强制生成dump快照
- pid: 需要打印配置信息的进程ID。
- executable: 产生核心dump的Java可执行文件。
- core: 需要打印配置信息的核心文件。
- server-id 可选的唯一id,如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。
- remote server IP or hostname 远程调试服务器的地址或主机名
- option: 选项参数
参考资料: http://www.hollischuang.com/archives/303
4. jhat
- jhat 用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言
- 语法
- jhat [-stack ] [-refs ] [-port ] [-baseline ] [-debug ] [-version] [-h|-help]
- -J< flag > :因为 jhat 命令实际上会启动一个JVM来执行, 通过 -J
可以在启动JVM时传入一些启动参数. 例如, -J-Xmx512m 则指定
运行 jhat 的Java虚拟机使用的最大堆内存为 512 MB. 如果需要使
用多个JVM启动参数,则传入多个 -Jxxxxxx. - -stack false|true:关闭对象分配调用栈跟踪(tracking object allocation call stack)。 如果分配位置信息在堆转储中不可用. 则必须将此标志设置为 false. 默认值为 true.
- -refs false|true :关闭对象引用跟踪(tracking of references to objects)。 默认值为 true. 默认情况下,返回的指针是指向其他特定对象的对象,如反向链接或输入引用(referrers or incoming references), 会统计/计算堆中的所有对象。
- -port port-number :设置 jhat HTTP server 的端口号. 默认值7000。
- -exclude exclude-le :指定对象查询时需要排除的数据成员列表文件(a le that lists data members that should be excluded from the reachable objects query)。 例如, 如果文件列列出了java.lang.String.value, 那么当从某个特定对象 Object o 计算可达的对象列表时, 引用路径涉及 java.lang.String.value的都会被排除。-baseline exclude-le :指定一个基准堆转储(baseline heap dump)。在两个 heap dumps 中有相同 object ID 的对象会被标记为不是新的(marked as not being new). 其他对象被标记为新的(new). 在比较两个不同的堆转储时很有用。
- -debug int:设置 debug 级别. 0 表示不输出调试信息。 值越大则表示输出更详细的 debug 信息。
- -version:启动后只显示版本信息就退出
- -J< flag > :因为 jhat 命令实际上会启动一个JVM来执行, 通过 -J
- jhat [-stack ] [-refs ] [-port ] [-baseline ] [-debug ] [-version] [-h|-help]
参考资料: https://www.cnblogs.com/myna/p/7590620.html
5. jstack
- jstack 用于打印出给定的java进程ID或core le或远程调试服务的Java堆栈信息
- 语法
- jstack [-l]
- jstack -F [-m] [-l]
- jstack [-m] [-l]
- jstack [-m] [-l] [[email protected]]
- -F 执行线程转储
- -m 打印java和本地帧
- -l打印列表信息,包括锁相关的信息
6. jinfo
- jinfo 用来查看 Java 进程运行的 JVM 参数
- 语法
- jinfo [option]
- jinfo [option] <executable
- jinfo [option] [[email protected]]
- option:选项参数
- no option 输出全部的参数和系统属性
- -€flag name 输出对应名称的参数
- -€flag [+|-]name 开启或者关闭对应名称的参数
- -€flag name=value 设定对应名称的参数
- -€flags 输出全部的参数
- -sysprops 输出系统属性
- pid 对应jvm的进程id
- executable core 产生core dump文件
- [[email protected]]remote server IP or hostname 远程的ip或者hostname,server-id标记服务的唯一性id
- option:选项参数
7. NMT
我们可以使用jdk中的本机内存跟踪工具(NMT)来跟踪JVM内部的内存使用,并可以通过jcmd命令来访问。不过,根据Java官方文档,开启NMT会有5%-10%的性能损耗。
-
开启NMT
NMT功能默认关闭,可以通过以下方式开启:
-XX:NativeMemoryTracking=[off | summary | detail]
-
访问NMT监控数据
JDK提供了jcmd命令来访问NMT数据:
jcmd VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]
3.2 jvm参数
-Xms
-Xmx
-XX:MetaspaceSize
-XX:MaxMetaspaceSize
-XX:+TraceClassUnloading/-XX:+TraceClassLoading(或者-verbose:class或者直
接-verbose)
3.3 内存分析工具
内存分析工具常用有JVisualVM、Eclipse Memory Analyzer。
- Eclipse Memory Analyzer
Action标签:- Histogram可以列出内存中的对象,对象的个数以及大小
- Dominator Tree可以列出线程,以及线程下面的那些对象占用的空间
- Top consumers通过图形列出最大的object
- Leak Suspects通过MA自动分析泄漏的原因
另外提几个重要的概念 - Shallow Heap :一个对象内存的消耗大小,不包含对其他对象的引用
- Retained Heap :是shallow Heap的总和,也就是该对象被GC之后所能回收到内存的总和。Retained Heap=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用)
- outgoing reference:当前对象引用的所有对象
- incoming reference:拥有当前对象的引用的所有对象
4.cvalarm组件内存情况
使用NMT跟踪cvalarm-alarm服务的内存使用情况
可以看到输出主要有两部分:Total和Virtual memory map
Total部分:整个memory主要包含了Java Heap、Class、Thread、Code、GC、Compiler、Internal Other、Symbol、Native Memory Tracking、Arena Chunk这几部分;其中reserved表示应用可用的内存大小,committed表示应用正在使用的内存大小。
- Java Heap:Reserved占用了1536MB(即-Xmx值),committed占用512MB(即-Xms)。另外说一点,java heap都是通过mmap分配空间的,malloc是指调用操作系统malloc函数直接分配内存,只有实际赋值后才占用,例如NIO中的allocateDirect
- Class:表示已经加载的classes个数为15894,其metadata占用了82MB;
- Thread:表示目前有131个线程,占用了130MB;
- Code:表示compiler生成code的时候占用了25M;
- GC:表示目前已经占用了62MB的内存空间用于帮助GC;
- Internal:表示命令行解析、JVMTI等占用了73MB;
- Symbol:表示诸如string table及constant pool等symbol占用了10MB;8. Native Memory Tracking:表示该功能自身占用了5MB;
- Other:表示其他剩余部分的占用了0MB;
Virtual memory map部分:就是Java进程的地址空间的每一段是用来干什么的,大小是多
少。这些进程空间段按照用途分可以分为以下几种 - Reserved for Class :Class分配的进程空间
- Reserved for Heap:java堆
- Reserved for Internal
- Reserved for Thread Stack
- Reserved for Code
- Reserved for GC
- Reserved for Unknown
5.组件内存溢出案例分析
《metaspace内存溢出问题排查》
《堆空间内存溢出问题排查》