jmap
java.lang.OutOfMemoryError: Java heap space
当应用程序申请更多的内存,而Java堆内存已经无法满足应用程序对内存的需要,就会抛出此种异常。
java.lang.OutOfMemoryError: PermGen space及其解决方法
表示Java永久带(方法区)空间不够,永久带用于存放类的字节码和长常量池,类的字节码加载后存放在这个区域,这和存放对象实例的堆区是不同的,大多数JVM的实现都不会对永久带进行垃圾回收,因此,只要类加载的过多就会出现这个问题。一般的应用程序都不会产生这个错误,然而,对于Web服务器来讲,会产生有大量的JSP,JSP在运行时被动态的编译成Java Servlet类,然后加载到方法区,因此,太多的JSP的Web工程可能产生这个异常。
java.lang.OutOfMemoryError: unable to create new native thread
创建了太多的线程,而能创建的线程数是有限制的,导致了这种异常的发生。
在Linux下线程使用轻量级进程实现的,因此线程的最大数量也是操作系统允许的进程的最大数量。
java.lang.OutOfMemoryError:GC overhead limit exceeded
是在并行或者并发回收器在GC回收时间过长、超过98%的时间用来做GC并且回收了不到2%的堆内存,然后抛出这种异常进行提前预警,用来避免内存过小造成应用不能正常工作。
java.lang.StackOverflowError ...
JVM的线程由于递归或者方法调用层次太多,占满了线程堆栈而导致的,线程堆栈默认大小为1M。
java.net.SocketException: Too many open files
由于系统对文件句柄的使用是有限制的,而某个应用程序使用的文件句柄超过了这个限制,就会导致这个问题。
工具使用:
jmap命令(Java Memory Map)
map命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小等等。可以使用jmap生成Heap Dump。
JVM Memory Map命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候自动生成dump文件。 jmap不仅能生成dump文件,还可以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。
常用命令:
-dump
dump堆到文件,format指定输出格式,live指明是活着的对象,file指定文件名,:live
是可选的。
[root@localhost jdk1.7.0_79]
# jmap -dump:live,format=b,file=dump.hprof 24971
Dumping heap to
/usr/local/java/jdk1
.7.0_79
/dump
.hprof ...
Heap dump
file
created
可以将24971
进程的内存heap以二进制形式(format=b)
输出出来到dump.hprof
文件里,再配合MAT(内存分析工具)。
-heap
打印heap的概要信息,GC使用的算法,heap的配置及使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况Attaching to process ID 8633, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.66-b17
using parallel threads in the new generation. ##新生代采用的是并行线程处理方式
using thread-local object allocation.
Concurrent Mark-Sweep GC ##同步并行垃圾回收
Heap Configuration: ##堆配置情况
MinHeapFreeRatio = 40 ##最小堆使用比例 (GC后如果发现空闲堆内存占到整个预估堆内存的N%(百分比), 则放大堆内存的预估最大值)
MaxHeapFreeRatio = 70 ##最大堆可用比例 (GC后如果发现空闲堆内存占到整个预估堆内存的N%(百分比),则收缩堆内存的预估最大值, 预估堆内存是堆大小动态调控的重要选项之一. 堆内存预估最大值一定小于或等于固定最大值(-Xmx指定的数值). 前者会根据使用情况动态调大或缩小, 以提高GC回收的效率)
MaxHeapSize = 1073741824 (1024.0MB) ##最大堆空间大小 (即-Xmx, 堆内存大小的上限)
NewSize = 89456640 (85.3125MB) ##新生代分配大小(新生代预估堆内存占用的默认值)
MaxNewSize = 348913664 (332.75MB) ##最大可新生代分配大小(新生代占整个堆内存的最大值)
OldSize = 178978816 (170.6875MB) ##老年代大小(老年代的默认大小, default size of the tenured generation)
NewRatio = 2 ##新生代比例(老年代对比新生代的空间大小, 比如2代表老年代空间是新生代的两倍大小. The ratio of old generation to young generation.)
SurvivorRatio = 8 ##新生代与suvivor的比例(Eden/Survivor的值. 这个值的说明, 很多网上转载的都是错的. 8表示Survivor:Eden=1:8, 因为survivor区有2个, 所以Eden的占比为8/10. Ratio of eden/survivor space size. -XX:SurvivorRatio=6 sets the ratio between each survivor space and eden to be 1:6, each survivor space will be one eighth of the young generation.
)
MetaspaceSize = 21807104 (20.796875MB) ##JDK1.8的方法区大小(分配给类元数据空间的初始大小(Oracle逻辑存储上的初始高水位,the initial high-water-mark ). 此值为估计值. MetaspaceSize设置得过大会延长垃圾回收时间. 垃圾回收过后, 引起下一次垃圾回收的类元数据空间的大小可能会变大)
CompressedClassSpaceSize = 1073741824 (1024.0MB)(类指针压缩空间大小, 默认为1G)
MaxMetaspaceSize = 17592186044415 MB ##JDK1.8的方法区最大(是分配给类元数据空间的最大值, 超过此值就会触发Full GC. 此值仅受限于系统内存的大小, JVM会动态地改变此值)
G1HeapRegionSize = 0 (0.0MB)(G1区块的大小, 取值为1M至32M. 其取值是要根据最小Heap大小划分出2048个区块. With G1 the Java heap is subdivided into uniformly sized regions. This sets the size of the individual sub-divisions. The default value of this parameter is determined ergonomically based upon heap size. The minimum value is 1Mb and the maximum value is 32Mb. Sets the size of a G1 region. The value will be a power of two and can range from 1MB to 32MB. The goal is to have around 2048 regions based on the minimum Java heap size.)
Heap Usage:
New Generation (Eden + 1 Survivor Space): ##新生代(伊甸区 + survior空间)
capacity = 80543744 (76.8125MB)
used = 34725560 (33.11687469482422MB) ##已经使用大小
free = 45818184 (43.69562530517578MB) ##剩余容量
43.11391335371745% used
Eden Space:
capacity = 71630848 (68.3125MB)
used = 29683880 (28.308753967285156MB)
free = 41946968 (40.003746032714844MB)
41.44007900060041% used
From Space:
capacity = 8912896 (8.5MB)
used = 5041680 (4.8081207275390625MB)
free = 3871216 (3.6918792724609375MB)
56.56612620634191% used
To Space:
capacity = 8912896 (8.5MB)
used = 0 (0.0MB)
free = 8912896 (8.5MB)
0.0% used
concurrent mark-sweep generation: ##老年代使用情况
capacity = 178978816 (170.6875MB)
used = 134752632 (128.51012420654297MB)
free = 44226184 (42.17737579345703MB)
75.28971026381133% used
36267 interned Strings occupying 3977048 bytes.
jmap -finalizerinfo pid
B
byte
C
char
D
double
F
float
I
int
J
long
Z
boolean
[ 数组,如[I表示
int
[]
[L+类名 其他对象
如果还需要看更加详细的信息,则使用:
jmap -dump:format=b,file=dumpFileName pid 直接生成在执行命令时所在的目录下面。
dump出来的文件可以用MAT、VisualVM等工具查看,也可以使用jhat。
jhat -port 9999 dumpFileName
如果dump出来的文件过大,可能需要指定Xmx(jhat实际启动了一个web应用)。
jhat -J-Xmx1000m -port 9999 dumpFileName
启动成功后,则可以通过浏览器查看:
ip:port
例如上图查询的是长度大于256的int数组。
jmap -histo:live 24971 | grep com.yuhuo 查询类名包含com.yuhuo的信息
jmap -histo:live 24971 | grep com.yuhuo > histo.txt 保存信息到histo.txt文件
-F 强迫.在pid没有响应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效.
-h | -help 打印辅助信息
-J<flag> 传递参数给jmap启动的jvm.
pid 需要被打印配相信息的java进程id,可以用jps查问
从安全点日志看,从Heap Dump开始,整个JVM都是停顿的,考虑到IO(虽是写到Page Cache,但或许会遇到background flush),几G的Heap可能产生几秒的停顿,在生产环境上执行时谨慎再谨慎。
live的选项,实际上是产生一次Full GC来保证只看还存活的对象。有时候也会故意不加live选项,看历史对象。
Dump出来的文件建议用JDK自带的VisualVM或Eclipse的MAT插件打开,对象的大小有两种统计方式:
- 本身大小(Shallow Size):对象本来的大小。
- 保留大小(Retained Size): 当前对象大小 + 当前对象直接或间接引用到的对象的大小总和。
看本身大小时,占大头的都是char[] ,byte[]之类的,没什么意思(用jmap -histo:live pid 看的也是本身大小)。所以需要关心的是保留大小比较大的对象,看谁在引用这些char[], byte[]。
(MAT能看的信息更多,但VisualVM胜在JVM自带,用法如下:命令行输入jvisualvm,文件->装入->堆Dump->检查 -> 查找20保留大小最大的对象,就会触发保留大小的计算,然后就可以类视图里浏览,按保留大小排序了)
转自:http://www.cnblogs.com/gaojk/articles/3886503.html
4、数据分析
总结
1.如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
2.要制作堆Dump可以直接使用jvm自带的jmap命令
3.可以先使用jmap -heap
命令查看堆的使用情况,看一下各个堆空间的占用情况。
4.使用jmap -histo:[live]
查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
5.也可以使用 jmap -dump:format=b,file=<fileName>
命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。
7.在用cms gc的情况下,执行jmap -heap有些时候会导致进程变T,因此强烈建议别执行这个命令,如果想获取内存目前每个区域的使用状况,可通过jstat -gc或jstat -gccapacity来拿到。
Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can’t attach to the process
在ubuntu中第一次使用jmap会报错:Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process
,这是oracla文档中提到的一个bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7050524,解决方式如下:
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope 该方法在下次重启前有效。
永久有效方法 sudo vi /etc/sysctl.d/10-ptrace.conf 编辑下面这行: kernel.yama.ptrace_scope = 1 修改为: kernel.yama.ptrace_scope = 0 重启系统,使修改生效。
写了一个OOM。
public class OOM {
public static void main(String[] args) {
List<StateMachine> list = new ArrayList<StateMachine>();
while(true){
list.add(new StateMachine());
}
}
}
使用如下配置:
-server
-Xms1024m
-Xmx1024m
-Xmn384m
-XX:+UseParallelOldGC
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/yp-tc-m-2777/Desktop/heap.bin