文章目录
- 一、前言
- 二、命令介绍
- 三、使用实例
- 1、jmap -heap [pid]
- 2、jmap -histo[:live] [pid]
- 3、jmap -histo[:live] [pid] |grep "[关键字1]\|[关键字2]"
- 4、jmap -dump:live,format=b,file=a.log [pid]
- 四、总结
一、前言
jdk安装后会自带一些小工具,jmap命令(Java Memory Map)是其中之一。主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。
jmap
命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等。可以使用jmap生成Heap Dump。
java memory = direct memory(直接内存) + jvm memory(MaxPermSize +Xmx)
1)直接内存跟堆
直接内存
则是一块由程序本身管理的一块内存空间,它的效率要比标准内存池要高,主要用于存放网络通信时数据缓冲和磁盘数据交换时的数据缓冲。
DirectMemory
容量可以通过 -XX:MaxDirectMemorySize
指定,如果不指定,则默认为与Java堆的最大值(-Xmx指定)一样。但是,在OSX上的最新版本的 JVM,对直接内存的默认大小进行修订,改为“在不指定直接内存大小的时默认分配的直接内存大小为64MB”,可以通过 -XX:MaxMemorySize
来显示指定直接内存的大小。
2)堆(Heap)和非堆(Non-heap)内存
按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。
可以看出JVM主要管理两种类型的内存:堆和非堆
。
堆
就是Java代码可及的内存,是留给开发人员使用的;非堆
就是JVM留给自己用的。
所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
3)栈与堆
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。
Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等 指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时 动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类 型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。
线程占用大小在MaxPermSize中进行内存申请和分配。
二、命令介绍
Usage:jmap [option] <pid>(to connect to running process)jmap [option] <executable <core>(to connect to a core file)jmap [option] [server_id@]<remote server IP or hostname>(to connect to remote debug server)where <option> is one of:<none> to print same info as Solaris pmap-heap to print java heap summary-histo[:live] to print histogram of java object heap; if the "live"suboption is specified, only count live objects-clstats to print class loader statistics-finalizerinfo to print information on objects awaiting finalization-dump:<dump-options> to dump java heap in hprof binary formatdump-options:live dump only live objects; if not specified,all objects in the heap are dumped.format=b binary formatfile=<file> dump heap to <file>Example: jmap -dump:live,format=b,file=heap.bin <pid>-F force. Use with -dump:<dump-options> <pid> or -histoto force a heap dump or histogram when <pid> does notrespond. The "live" suboption is not supportedin this mode.-h | -help to print this help message-J<flag> to pass <flag> directly to the runtime system
注:
-heap
:打印jvm heap的情况-histo
:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。-histo:live
:同上,但是只答应存活对象的情况-permstat
:打印permanent generation heap情况
三、使用实例
1、jmap -heap [pid]
展示pid的整体堆信息。
首先启动一个tomcat,然后使用如下命令获取tomcat的进程ID。
ps -ef|grep tomcat
然后执行:
jmap -heap 86038
输出内容:
Attaching to process ID 86038, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13using thread-local object allocation.
Parallel GC with 4 thread(s)Heap Configuration: # 堆内存初始化配置MinHeapFreeRatio = 0 # -XX:MinHeapFreeRatio设置JVM堆最小空闲比率 MaxHeapFreeRatio = 100 # -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率 MaxHeapSize = 2147483648 (2048.0MB) # -XX:MaxHeapSize=设置JVM堆的最大大小NewSize = 44564480 (42.5MB) # -XX:NewSize=设置JVM堆的‘新生代’的默认大小MaxNewSize = 715653120 (682.5MB) # -XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小OldSize = 89653248 (85.5MB) # -XX:OldSize=设置JVM堆的‘老生代’的大小NewRatio = 2 # -XX:NewRatio=:‘新生代’和‘老生代’的大小比率SurvivorRatio = 8 # -XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB)Heap Usage:
PS Young Generation
Eden Space: # Eden区内存分布capacity = 426770432 (407.0MB)used = 79056192 (75.39385986328125MB)free = 347714240 (331.60614013671875MB)18.524289892698096% used
From Space: # 其中一个Survivor区的内存分布capacity = 30932992 (29.5MB)used = 0 (0.0MB)free = 30932992 (29.5MB)0.0% used
To Space: # 另一个Survivor区的内存分布capacity = 31457280 (30.0MB)used = 0 (0.0MB)free = 31457280 (30.0MB)0.0% used
PS Old Generationcapacity = 72351744 (69.0MB)used = 21741336 (20.734153747558594MB)free = 50610408 (48.265846252441406MB)30.049498184867527% used16180 interned Strings occupying 2074344 bytes.
2、jmap -histo[:live] [pid]
展示class的内存情况。
jmap -histo 86038
执行结果:
num #instances #bytes class name
----------------------------------------------1: 7287 46167552 [I2: 78166 33933008 [B3: 221419 25746168 [C4: 116110 2786640 java.lang.String5: 11708 886224 [Ljava.lang.Object;6: 18869 603808 java.util.HashMap$Node7: 24275 582600 java.lang.StringBuilder8: 6464 568832 java.lang.reflect.Method9: 4715 541352 java.lang.Class10: 4847 418760 [S11: 1686 391288 [Ljava.util.HashMap$Node;...
注:
instances
:实例数;bytes
:内存占用大小;classs name
:类名。
它基本是按照使用使用大小逆序排列的。
jmap -histo:live 86038
获取所有生存的对象的内存情况。
注:
- 该命令获取的结果与jmap -histo [pid]获取结果一致;
执行jmap -histo:live [pid]时,JVM会先触发gc,然后再统计信息
。
从打印结果可看出,类名中存在[C、[B等内容,只知道它占用了那么大的内存,但不知道由什么对象创建的。下一步需要将其他dump出来,使用内存分析工具进一步明确它是由谁引用的、由什么对象。
另外可以执行如下命令将打印内容保存到文件中。
jmap -histo:live 86038>a.log
通过对多次打印内容的对比,可以对比出GC回收了哪些对象。
3、jmap -histo[:live] [pid] |grep “[关键字1]|[关键字2]”
展示指定关键字的类的内存情况。
jmap -histo:live 86038 | grep "java.util.\|java.lang."
执行结果:
num #instances #bytes class name
----------------------------------------------6: 18869 603808 java.util.HashMap$Node7: 24275 582600 java.lang.StringBuilder8: 6464 568832 java.lang.reflect.Method9: 4715 541352 java.lang.Class...
从执行结果看,打印出了类全路径名称中包含 java.util.
或 java.lang.
的所有类内存情况。
注:匹配多个关键字之间用
\|
隔开。
4、jmap -dump:live,format=b,file=a.log [pid]
内存信息dump到a.log文件中。
这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用
。
该命令通常用来分析内存泄漏OOM,通常做法是:
1)首先配置JVM启动参数,让JVM在遇到OutOfMemoryError时自动生成Dump文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path
2)然后使用命令
// 如果只dump heap中的存活对象,则加上选项-live。
jmap -dump:format=b,file=/path/heap.bin [pid]
四、总结
-
1、如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
-
2、要制作堆Dump可以直接使用jvm自带的jmap命令
-
3、可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。
-
4、使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
-
5、也可以使用 jmap -dump:format=b,file=命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
-
6、在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。
-
7、在用cms gc的情况下,执行jmap -heap有些时候会导致进程变T,因此强烈建议别执行这个命令,如果想获取内存目前每个区域的使用状况,可通过jstat -gc或jstat -gccapacity来拿到。