使用了标志-XX:+HeapDumpOnOutOfMemoryError,JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中。
对如下一段代码,【代码1】
- public static void main(String[] args) {
- long arr[];
- for (int i=1; i<=10000000; i*=2) {
- arr = new long[i];
- }
- }
设置虚拟机参数为:-Xmx40m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\Java\dump
执行程序,很快会抛出异常:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to E:\Java\dump\java_pid10400.hprof ...
Heap dump file created [1192880 bytes in 0.024 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
奇怪的是Heap dump file只有一兆多一点,用JProfiler打开这个文件,并没有看到导致内存溢出的long[];刚开始以为是堆转储时,JVM会忽略掉只被线程栈引用的数组,进一步测试,发现并不是这个原因;查看相应的class文件,反编译后得到:
【将代码1通过JDK1.6编译后的字节码反编译后得到的代码】
- public static void main(String[] args)
- {
- for (int i = 1; i <= 10000000; i *= 2) {
- long[] arr = new long[i];
- }
- }
其中long[] arr 的定义从循环外面变到了循环里面,应该是编译器进行了优化,这样修改后,功能并没有变化,但long[] arr的生存范围变小了,生存范围是从声明到本次循环结束;每次循环开始时,在线程栈中声明一个指向long[]的引用 arr,然后在堆中创建一个指定大小的long[],把它的引用赋给arr;在每次循环结束,进入下次循环前,线程栈中的引用arr就会被销毁,它所指向的long[]就变成了没有被引用的实例;进入了下次循环,又重新在线程栈中声明一个引用arr,在堆中创建一个指定大小的long[],把它的引用赋给arr。
分析:虚拟机参数配置了-Xmx40m,在堆内存的使用量超过40M时,虚拟机就会抛出OutOfMemoryError: Java heap space,同时将堆内存转储到文件中,这时候前面循环创建的long[]实例没有被引用,应该已经被垃圾回收,所以Heap dump file中没有程序创建的long[]实例。
在源代码arr = new long[i];的后面加上显示堆内存使用的语句:
- System.out.println("size : " + i);
- Runtime runtime = Runtime.getRuntime();
- System.out.printf("maxMemory : %.2fM\n", runtime.maxMemory()*1.0/1024/1024);
- System.out.printf("totalMemory : %.2fM\n", runtime.totalMemory()*1.0/1024/1024);
- System.out.printf("freeMemory : %.2fM\n", runtime.freeMemory()*1.0/1024/1024);
可以看出每次执行arr = new long[i];后堆内存的使用情况;配置-Xmx40m的情况下,抛出异常前最后一次正常执行的循环的输出信息为:
size : 4194304
maxMemory : 39.75M
totalMemory : 38.50M
freeMemory : 6.27M
将代码1改为:【代码2】
- public static void main(String[] args) {
- long arr[] = {};
- for (int i=1; i<=10000000; i*=2) {
- arr = new long[i];
- }
- }
查看相应的class文件,反编译后得到:
【将代码2通过JDK1.6编译后的字节码反编译后得到的代码】
- public static void main(String[] args)
- {
- long[] arr = new long[0];
- for (int i = 1; i <= 10000000; i *= 2) {
- arr = new long[i];
- }
- }
其中long[] arr 的定义跟源代码一致,是在循环外面,应该是因为有初始化的代码,没办法再把定义移到循环里面;这种情况下,arr的生存范围是从声明到程序结束;每次循环开始时,会在堆中创建一个指定大小的long[],把它的引用赋给arr,这样arr之前指向的long[]就变成没有被引用的实例;进入了下次循环,在堆中创建一个指定大小的long[],把它的引用赋给arr,在赋值完成前,arr指向上次循环创建的long[]实例,赋值完成后,arr就指向本次循环创建的long[]实例,这时候上次循环创建的long[]实例就变成没有被引用的实例。
设置虚拟机参数为:-Xmx40m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\Java\dump
执行程序,很快会抛出异常:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to E:\Java\dump\java_pid11020.hprof ...
Heap dump file created [17970188 bytes in 0.172 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Heap dump file有十多兆,用JProfiler打开这个文件,可以找到一个占用16M内存的long[]。
分析:在程序因为没有足够的堆内存创建实例而抛出OutOfMemoryError时,引用arr仍然指向上次循环创建的long[]实例,在JVM将堆内存转储到文件中时,会把这个long[]实例也考虑进去;这个long[]实例被arr引用,arr位于线程栈中,所以上图中显示long[]实例被java stack引用。
在源代码arr = new long[i];的后面加上显示堆内存使用的语句,
可以看到抛出异常前最后一次正常执行的循环的输出信息为:
size : 2097152
maxMemory : 39.75M
totalMemory : 33.26M
freeMemory : 8.92M
最后一次正常创建的long[]的size为2097152,占用了16M内存,而代码1执行时最后一次正常创建的long[]的size为4194304,占用了32M内存。
分析:代码1在循环中创建long[]实例时,上次循环创建的long[]实例没有被引用,可以被垃圾回收掉,所以在参数Xmx40m下,代码1创建占用32M内存的long[]还是可以正常执行的,试图创建占用64M内存的long[]才抛出异常;代码2在循环中创建long[]实例时,上次循环创建的long[]实例还在被arr引用,不能被垃圾回收掉,代码2在创建占用16M内存的long[]实例时,前一个循环创建的占8M内存的long[]实例还不能被回收,8+16=24 < 40,所以这次能够正常执行,下一个循环要尝试创建占32M内存的long[]实例,这时候占16M内存的long[]实例还不能被回收,16+32=48>40,堆内存不够用,只好抛出异常。
小结:通过分析OutOfMemoryError时生成的堆转储文件,有助于找到内存不够用的原因;如果生成的堆转储文件的大小跟最大堆内存的配置有很大差异,就要分析抛出异常的代码,查找原因,还可以将字节码反编译,看看编译器是不是对代码的结构进行了调整。
转自:http://epy.iteye.com/blog/1914455
相关推荐
使用 Eclipse Memory Analyzer 进行堆转储文件分析_files
对于大型 JAVA 应用程序来说,再精细的测试也难以堵住所有的漏洞,即便我们在...JVM 能够记录下问题发生时系统的部分运行状态,并将其存储在堆转储 (Heap Dump) 文件中,从而为我们分析和诊断问题提供了重要的依据。
将来,它将对堆文件进行更复杂的计算和分析。 #当前状态: 仍然是一个在制品。 这是可以扩展以实现我们最终目标的工作基础。 但是当前结果尚未得到验证(或至少与其他程序不匹配),并且使用情况充其量是笨拙的...
UB转储订单的配置和操作。介绍工厂间UB转储,带交货单的UB转储订单操作。对应配置过程,测试过程等,内容比较全
windows 应用程序崩溃时的内存转储及dump文件的分析
JAVA之hashmap源码分析 无头Android堆分析器 ...转储堆后,使用HAHA对其进行分析和分析。 DataBuffer buffer = new MemoryMappedFileBuffer (heapDumpFile); Snapshot snapshot = Snapshot . createSnapshot(buffer
快速转储和加载数据库
- 第4章 虚拟机性能监控、故障处理工具- 4.2 基础故障处理工具JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来
本书详细深入解析windows操作系统之崩溃转储分析
一个供非专业人员创建崩溃和挂起转储文件的小工具. 此版本无法更新,最新版请到http://code.google.com/p/easytools/下载。简介见http://blog.csdn.net/LocalVar/archive/2009/01/06/3721850.aspx
WIP Sinatra / AR应用程序可从Ruby的ObjectSpace.dump_all查看/分析堆转储。 笏 这是一个简单的基于sinatra和ActiveRecord的Web应用程序,旨在在本地运行。 它具有用于将堆转储导入postgres DB(或可能是任何AR适应...
oracle 数据库转储文件对于大家研究oracle数据库有很大帮助,但是如何转储是很多人遇到的问题,希望此教程能够给大家提供帮助。
Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的统计信息,提供关于锁定监视器...
使用 Memory Analyzer 来分析生产环境的 Java 堆转储文件,可以从数以百万计的对象中快速计算出对 象的 Retained Size,查看是谁在阻止垃圾回收,并自动生成一个 Leak Suspect(内存泄露可疑点) 报表。 Memory ...
转储库Windows 内核转储分析器
pb9开发,将mp3歌曲名称 以列表方式转储到excel中,然后打印歌曲名称。
MemoScope.Net 转储和分析.Net应用程序内存维基请阅读以了解有关MemoScope的所有信息。 许多图片,GIF动画和一些用于每个主要功能的文本。TLDR它的MemoScope.Net是什么? 它是分析.Net进程内存的工具:它可以将应用...
IBM出的一款堆内存对象分析ha(heap analyser)工具,可以用来分析websphere生成的内存转储phd格式文件。 使用方法 1.下载ha457.jar 2.java -Xmx4g -jar ha456.jar 3.打开heapdump.phd 4.他就会显示下面的图片,出现...
转储postgis语句,可以远程登录postgis然后执行语句后导出导入postgis数据库 转储postgis语句,可以远程登录postgis然后执行语句后导出导入postgis数据库
利用Windows内存转储文件对挥发性数据取证,张中文,曹天杰,本文给出了如何对挥发性内存数据进行取证获取有用信息,描述了崩溃转储的配置步骤和转储文件的分析过程。使用Microsoft Debugging Tools��