本篇文章理解源自于《深入理解java虚拟机》2.4章节 实战:OutOfMemoryError异常
 
在以下例子中,所有代码都可以抛出OutOfMemoryError异常,但是要区分到底是内存泄漏(Memory Leak)还是内存溢出(Memory Overflow),我们需要借助Eclipse Memory Analyzer(也成为MAT,mat是一个分析Java内存的神器)插件来分析.hprof文件才能得知。
安装Eclipse Memory Analyzer插件方法:eclipse -> Help -> Eclipse Marketplace -> search框搜索“Memory Analyzer” -> 点击install即可安装。
IBM官方介绍Eclipse Memory Analyzer网址:https://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html?ca=drs-
 
在测试以下例子之前,我们来了解下VM参数是个什么东西:
VM arguments参数解析
-verbose:gc 参数表示将在控制台输出full GC详细,例如:[Full GC 168K->97K(1984K), 0.0253873 secs]
-Xms20M 初始堆大小。默认值:物理内存的1/64(<1GB)。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。
-Xmx20M 最大堆大小。默认值:物理内存的1/4(<1GB)。默认空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
-Xmn10M 年轻代大小。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:+PrintGCDetails 打印full gc的详细信息。
-XX:+HeapDumpOnOutOfMemoryError JVM 就会在发生内存泄露时抓拍下当时的内存状态,也就是我们想要的堆转储文件。也就是会在项目根目录下生成*.hprof文件可供分析。
-XX:+HeapDumpOnCtrlBreak 如果你不想等到发生崩溃性的错误时才获得堆转储文件,也可以通过设置如下 JVM 参数来按需获取堆转储文件。
-XX:SurvivorRatio=8 Eden区与Survivor区的大小比值。设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
VM参数参考该文章:JVM系列三:JVM参数设置、分析(http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
 
new一个Junit4单元测试类VirtualTest,依次放入以下例子代码:
例子1
/**
* 以Run AS -> Run Configurations运行。
* 并设置 VM arguments参数如下:
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError
*/
@Test
public void testOutOfMemoryError() {
  List<VirtualTest> list = new ArrayList<VirtualTest>();
  while(true){
    list.add(new VirtualTest());
  }
}
执行代码后,控制台抛出异常如下:
[GC (Allocation Failure) [PSYoungGen: 8192K->1018K(9216K)] 8192K->2211K(19456K), 0.0078156 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 9210K->1000K(9216K)] 10403K->7720K(19456K), 0.0356040 secs] [Times: user=0.11 sys=0.00, real=0.04 secs]
…………
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid10604.hprof ...
Heap dump file created [29234562 bytes in 0.214 secs]
Heap
PSYoungGen total 9216K, used 8154K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 99% used [0x00000000ff600000,0x00000000ffdf6a10,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 9292K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 90% used [0x00000000fec00000,0x00000000ff513390,0x00000000ff600000)
Metaspace used 4979K, capacity 5182K, committed 5248K, reserved 1056768K
class space used 591K, capacity 626K, committed 640K, reserved 1048576K
异常分析
这个异常我们要借助Memory Analyzer工具来查看内存溢出的是哪个对象,刷新整个项目,点击根目录下的java_pid10604.hprof,默认是Overview视图,上面是一个显眼的饼图:
 
在这个饼图里面,我们可以看到有一个可疑对象在总共16.4MB的内存里,它占用了15.5MB的内存,占用了94.5%的内存。
在饼图的左下方,这个报告告诉我们这个内存溢出发生在main这个线程里面。
接下来我们当然想知道到底是哪个对象干的,那么找到饼图的下方还有一些工具:
点击Histogram链接,打开Histogram视图:
我们发现在Objets这一栏,最多的一个对象实例竟然达到810327个,这个对象类是com.virtual.VirtualTest。那么很清楚了,我们new出了太多这个类对象,导致垃圾内存溢出。
在dominator_tree视图中,可以看到main线程占用了94.02%的内存
在上图中,我们可以看到该.hprof文件存放的路径、大小、生成时间、格式、报告中总共产生多少对象,堆大小(16.4MB)等信息。
 
例子2
  /**
* 以Run AS -> Run Configurations运行。
* 并设置 VM arguments参数如下:
* -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory2(){
List<String> list = new ArrayList<String>();
for(int i=0;i<10000000;i++){
String str = new String();
list.add(str);
}
}
执行代码后,控制台抛出异常如下
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid3360.hprof ...
Heap dump file created [12525143 bytes in 0.136 secs]
异常分析
Sun 官方对此的定义是:“并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作。“
意思就是GC用尽了全力可还是来不及回收你new出来的那么多string对象,而且list对象越来越大,你还往里面塞对象,还让它活着,你还不让我回收,于是……呵呵……我挂了(>﹏<)
但是我抛出这个异常的目的就是在JVM彻底死了之前告诉你我挂了,好让你来得及实施一些紧急措施,比如紧急保存之类。
参考文章《java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得》http://www.cnblogs.com/hucn/p/3572384.html
 
总结
到目前为止,我还没能从Eclipse Memory Analyzer工具中发现能够区分出java内存泄漏(Memory Leak)和内存溢出(Memory Overflow)这两种状态的直接证据。
个人理解为:
java内存泄漏(Memory Leak) 当new出很多对象,而GC无法及时回收这些对象时,会导致内存泄漏。
内存溢出(Memory Overflow) 当一个对象长时间存活,且占用内存巨大直接威胁到总内存时,会导致内存溢出。
 
以下是其它已在JDK1.8版本中不再报内存溢出的代码:
举例1
  /**
* 在JDK1.8中不会抛出异常!!!!
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下:
* -Xss128k -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError() {
int stackLength = 1;
try {
while (true) {
stackLength = stackLength + 1;
System.out.println("stack length ==" + stackLength);
}
} catch (Throwable t) {
System.out.println("stack length : " + stackLength);
throw t;
}
}
举例2
  /**
* 在JDK1.8中不会抛出异常!!!! 但是会使操作系统假死。
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下:
* -Xss2M -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError2() {
while (true) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
}
}
});
t.start();
}
}
举例3
  /**
* 在JDK1.8中不会抛出异常!!!!
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下:
* -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError3() {
List<String> list = new ArrayList<String>();
int i=0;
while (true) {
list.add(String.valueOf(i));
}
}
举例4
  /**
* 在JDK1.8中不会抛出异常!!!!
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下: -Xms10m -Xmx10m
* -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError4() {
while(true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.getClass());
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() { @Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
return proxy.invoke(obj, args);
}
});
enhancer.create();
}
}
 
 
 
 

OutOfMemoryError异常java内存泄漏(Memory Leak)和内存溢出(Memory Overflow)的更多相关文章

  1. Memory Leak(内存泄漏)问题总结(转)

    最近听了一些关于Memory Leak(内存泄漏)的seminar,感觉有些收获,所以留个记录,并share给朋友. 1 什么是Memory Leak. Memory Leak是指由于错误或不完备的代 ...

  2. 堆(heap)和栈(stack)、内存泄漏(memory leak)和内存溢出

    来源:http://blog.itpub.net/8797129/viewspace-693648/ 简单的可以理解为:heap:是由malloc之类函数分配的空间所在地.地址是由低向高增长的.sta ...

  3. 安卓android WebView Memory Leak WebView内存泄漏

    Android WebView Memory Leak WebView内存泄漏 在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面 ...

  4. 【内存泄漏】 C/C++内存泄漏及其检测工具

    对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题.已经有许多技术被研究出来以应对这个问题,比如 Smart Pointer,Garbage Collection等.Smart Po ...

  5. Android WebView Memory Leak WebView内存泄漏

    在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面后,即使在包含该webview的Activity的destroy()方法中,使用 ...

  6. 什么是内存泄漏?(What is a memory leak?)

    程序中的内存泄漏是怎么回事呢? 我们写过很多带有关键词free()的程序.比如我在这篇博文关于链表的一些重要操作(Important operations on a Linked List)中删除整个 ...

  7. OutOfMemoryError异常——Java堆溢出。

    https://blog.csdn.net/en_joker/article/details/79726975 (将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过参数- ...

  8. _CrtSetBreakAlloc简单内存泄漏检测方法,解决Detected memory leaks!问题

    我的环境是: XP SP2 . VS2003 最近在一个项目中,程序退出后都出现内存泄漏: Detected memory leaks! Dumping objects -> {98500} n ...

  9. valgrind检查代码内存泄漏,5种内存泄漏情况

    摘要: valgrind是linux下用于调试程序和查找内存泄露的常用工具.valgrind会报告5种内存泄露,"definitely lost", "indirectl ...

随机推荐

  1. 『Python』内存分析_list和array

    零.预备知识 在Python中,列表是一个动态的指针数组,而array模块所提供的array对象则是保存相同类型的数值的动态数组.由于array直接保存值,因此它所使用的内存比列表少.列表和array ...

  2. 『TensorFlow』使用集合collection控制variables

    Variable Tensorflow使用Variable类表达.更新.存储模型参数. Variable是在可变更的,具有保持性的内存句柄,存储着Tensor 在整个session运行之前,图中的全部 ...

  3. 3、简单了解Angular应用的启动过程

    首先,了解一下目录结构: 然后,简明扼要的说一下应用的启动过程: 1.首先找到main.ts(模块启动入口),main.ts去找到app中的根模块app.module.ts 2.根模块app.modu ...

  4. python命名空间与作用域

    python命名空间与作用域   命名空间是名称与对象之间的关系,可以将命名空间看做是字典,其中的键是名称,值是对象. 命名空间不共享名称. 在命名空间中的名称能将任何python对象作为值,在不同的 ...

  5. nltk中的三元词组,二元词组

    在做英文文本处理时,常常会遇到这样的情况,需要我们提取出里面的词组进行主题抽取,尤其是具有行业特色的,比如金融年报等.其中主要进行的是进行双连词和三连词的抽取,那如何进行双连词和三连词的抽取呢?这是本 ...

  6. 跟随我在oracle学习php(6)

    CSS,主要用于控制Web页面的外观.通过使用CSS样式设置页面的风格,可将页面的内容 与表现形式分离.css  层叠样式表美化页面配合html布局. 在当前可以浏览的网站当中,都存在着CSS样式代码 ...

  7. PL/SQL修改数据之后,程序查到的还是原来的数据怎么办?

    我们在开发的过程中,可能需要手动删除或者修改数据库数据,但是在update之后,程序还是查到的是原始的数据.而我们自己在pl/sql中查到的确实修改之后的数据,通常情况下这个是因为我们在修改之后没有c ...

  8. Centos7部署kubernetes测试k8s应用(九)

    1.创建一个deployment [root@linux-node1 ~]# kubectl run net-test --image=alpine --replicas=2 sleep 360000 ...

  9. 关于pdfbox操作pdf的分享链接手长

    http://blog.csdn.net/fighting_no1/article/details/51038966

  10. 剑指Offer 17. 树的子结构 (二叉树)

    题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 题目地址 https://www.nowcoder.com/practice/6e196c44c7 ...