zoukankan      html  css  js  c++  java
  • JVM异常之:堆溢出OutofMemoryError

    1、堆溢出 

    Java 堆内存的OutOfMemoryError异常是实际应用中最常见的内存溢出异常情况。出现Java 堆内存溢出时,异常堆栈信息“java.lang.OutOfMemoryError”会跟着进一步提示“Java heap space”。

    要解决这个区域的异常,一般的手段是首先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump 出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。使用Eclipse Memory Analyzer 打开的堆转储快照文件。

    如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots 的引用链。于是就能找到泄漏对象是通过怎样的路径与GC Roots 相关联并导致垃圾收集器无法自动回收它们的。掌握了泄漏对象的类型信息,以及GC Roots 引用链的信息,就可以比较准确地定位出泄漏代码的位置。

    如果不存在泄漏,换句话说就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(-Xmx 与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

    以上是处理Java 堆内存问题的简略思路,处理这些问题所需要的知识、工具与经验在后面的几次分享中我会做一些额外的分析。

    示例:

    package com.dxz.jvm;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Described:堆溢出测试
     * @VM args:-verbose:gc -Xms20M -Xmx20M -XX:+PrintGCDetails
     */
    public class HeapOutOfMemory {
    
        public static void main(String[] args) {
            List<TestObject> cases = new ArrayList<TestObject>();
            HeapOutOfMemory heapOutOfMemory = new HeapOutOfMemory();
            while (true) {
                cases.add(heapOutOfMemory.new TestObject());
    
            }
        }
    
        public class TestObject {
            private double a = 34.53;
            private Integer b = 9999999;
        }
    }

    结果:

    2、常量池溢出(常量池都有哪些信息,我们在后续的JVM类文件结构中详细描述)

    2.1、方法区溢出(Perm持久代溢出)

     在jdk1.6及之前的版本中,常量池放在Perm区也即是方法区中,所以在jdk1.6版本中,常量池溢出可以说是方法区溢出。

    方法区溢出的示例见《JVM体系结构之七:持久代、元空间(Metaspace) Metaspace 之三--了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap

    在网络上看到一个cglib导致的方法区溢出的示例:

    package com.dxz.jvm;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    /**
     * @VM args : -XX:PermSize=10M -XX:MaxPermSize=10M
     */
    public class MethodAreaOutOfMemory {
    
        public static void main(String[] args) {
            while (true) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(MethodAreaOutOfMemory.TestObject.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
                        return arg3.invokeSuper(arg0, arg2);
                    }
                });
                enhancer.create();
            }
        }
    
        public static class TestObject {
            private double a = 34.53;
            private Integer b = 9999999;
        }
    }

    运行结果:

    1. Caused by: java.lang.OutOfMemoryError: PermGen space  
    2. at java.lang.ClassLoader.defineClass1(Native Method)  
    3. at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)  
    4. at java.lang.ClassLoader.defineClass(ClassLoader.java:616)  
    5. ... 8 more 

    方法区溢出也是一种常见的内存溢出异常,一个类要被垃圾收集器回收掉,判定条件是比较苛刻的。在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景除了上面提到的程序使用了CGLib字节码增强和动态语言之外,常见的还有:大量JSP或动态产生JSP文件的应用(JSP第一次运行时需要编译为Java类)、基于OSGi的应用(即使是同一个类文件,被不同的加载器加载也会视为不同的类)等。

    2.2、常量池导致的堆溢出

    在jdk1.7以后,perm持久代已经被移除,常量池也放到了堆中,其导致的溢出也变成了堆溢出。

    示例见《JVM体系结构之七:持久代、元空间(Metaspace) Metaspace 之三--了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap

  • 相关阅读:
    【BZOJ1093】【ZJOI2007】最大半联通子图 [DP][Tarjan]
    【BZOJ1926】【SDOI2010】粟粟的书架 [主席树]
    【BZOJ4069】【APIO2015】巴厘岛的雕塑 [贪心][DP]
    【BZOJ2815】【ZJOI2012】灾难 [LCA]
    【BZOJ3453】XLkxc [拉格朗日插值法]
    【BZOJ2850】巧克力王国 [KD-tree]
    《类和对象》
    @RequestMapping映射请求
    010-Python-socket编程
    SSH-Struts第二弹:一个Form提交两个Action
  • 原文地址:https://www.cnblogs.com/duanxz/p/8522435.html
Copyright © 2011-2022 走看看