zoukankan      html  css  js  c++  java
  • 对栈,堆,直接内存溢出和异常信息整理详解,以及JVM参数调优

    转载自:https://blog.csdn.net/BIackMamba/article/details/91046045【对一些错误做了修改和添加了调优参数配置】

    JVM调优:

    -Xms 为jvm启动时分配的内存,比如-Xms200m,表示分配200M
    -Xmx 为jvm运行过程中分配的最大内存,比如-Xms500m,表示jvm进程最多只能够占用500M内存
    -Xss 为jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M
    -server 这个基本不用设置,现代的JVM都是默认是-server模式
    启动命令写法为:java -jar -Xms256m -Xmx2048m -Xss256k -server xxx.jar
     

    OutOfMemoryError异常

    Java虚拟机中除了计数器外,虚拟机内存的其他几个运行时区域都有可能发生OutOfMemoryError异常
    Java堆溢出

        概述:
        Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。
        Java堆内存中常见的内存溢出异常情况。当出现Java堆内存溢出时,异常堆栈信息"java.lang.OutOfMemoryError”"会跟着进步提示“Java heap space”(Java堆空间)
        要解决这个区域的异常,一般的手段是先通过内存映像分析工具(如Eclipse MemoryAnalyzer)对Dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(MemoryLeak)还是内存溢出(MemoryOverflow).
        如果是内存泄露,可进一步通过工具在看泄露对象到GC Roots的引用链。于是就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的,掌握了泄露对象的类型信息及GC Roots引用链的信息,就可以比较准确地定位出泄露代码的位置,
        如果不存在泄露,换句话说,就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的维参数(-Xmx 与 -Xms),与机器物理内存对比看是否还可以调大,从代码上检查存在某些对象生命周期过长,持有状态时间过长的情况,尝试减少程序运行期的内存消耗
        举例:Java堆内存溢出异常测试

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     *  设置限制Java堆的大小为20MB,不可扩展(将堆的最小值-Xms参数与最大直-Xmx参数设置为样即可避免堆自动扩展)。
     *  通过参数-XX:+HeapDumpOnOutOfMemoryEror可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析。
     * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
     * @author xx
     *
     */
    public class Demo {
        
        static class OOMObject{
            
        }
    
        public static void main(String[] args) {
            List<OOMObject> list  =  new ArrayList<OOMObject>();
            while (true) {
                list.add (new OOMObject());
            }
        }
    }
    
    
    运行结果:
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:3210)
        at java.util.Arrays.copyOf(Arrays.java:3181)
        at java.util.ArrayList.grow(ArrayList.java:265)
        at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
        at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
        at java.util.ArrayList.add(ArrayList.java:462)


    Java虚拟机栈溢出

        由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于HotSpot米说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由-Xss参数设定。
        关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:
        如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError 异常。
        如果虚拟机在展栈时无法中请到足够的内存空间,则抛出OutOfMemoryError 异常.
        举例:虚拟机栈和本地方法栈OOM测试
        使用-Xss参数减少栈内存容量。结果:抛出StackOverflowError异常,异常出现时输出的堆线深度相应缩小。
        定义了大量的本地变量,增大此方法赖中本地变量表的长度。结果:抛出StackOverflowError异常时输出的堆栈深度相应缩小。

    /**
     * VM Args: -Xss128k
     * @author 邢鑫
     *
     */
    public class JavaVMStackSOF {
        
        private int stackLength = 1;
        public void stackLeak() {
            stackLength++ ;
            stackLeak () ;
        }
         public static void main(String[] args) throws Throwable {
    
             JavaVMStackSOF oom = new JavaVMStackSOF () ;
             try {
        
                 oom.stackLeak() ;
             }catch (Throwable e) {
        
                 System. out.printin("stack length:" + oom.stackLength);
                 throw e;
             }
    
         }
    }
    
    
    运行结果:
    stack length:2402
    
    Exception in thread "main" java. lang . StackOverflowError
    
          at org. fenixsoft. oom. VMStackSOF .leak (VMStackSOF . java:20)
          at org. fenixsoft.oom. VMStackSOF . leak (VMStackSOF . java:21)
          at org. fenixsoft. oom. VMStackSOF .leak (VMStacksoF. java :21 )


    实验结果表明:在单个线程下,无论是由于栈械太大还是虚拟机找容量太小,当内存天法分配的时候,虚批机地出的都是StackOverflowError异常。

        操作系统分配给每个进程的内存是有限制的,臂如32位的Windows限制为2GB.虚拟机提供了参数来控制Java堆和方法区的这两部分内存的最大值。剩余的内存为2GB (操作系统限制)减去Xmx (最大堆容量),再减去MaxPermSize (最大方法区容量),程序计数器消耗内存很小,可以忽略掉。如果虚拟机进程本身耗费的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈“瓜分”了。每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。
        如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换64位店拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。

    方法区和运行时常量池溢出

        在运行时常量池是方法区的一部分。
        String intermO是一个Native方法,它的作用是: 如果字符串常量池中已经包含一一个等于此Singl象的字符小期每同代真迪中这个字符串的Sring对象:否则,将此Sring对象仙省的字莉小能加州常晨池中并且返同此Smi象的引用。
        在IDK 16及之前的版本中,由于需量他分配在水久代内,我们可以通过xx PemSi和xx MaPomSie限制方法区大小,从而间接展制其中常量池的容量。
        举例:运行时常量池导致的内存溢出异常

    public class ConstantPoolOOM {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            
            int i =0;
            while(true){
                list.add(String.valueOf(i++).intern());
            }
        }
    }
    
    
    运行结果:
    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
        at java.lang.String.intern(Native Method)
        at org.fenixsoft.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18)


    从运行结果中可以看到,运行时常量池溢出,在OutOfMemoryError后面跟随的提示信息是“PermGen space",说明运行时常量池属于方法区(HotSpot 虚拟机中的永久代)的一部分。而使用JDK 1.7 运行这段程序就不会得到相同的结果,while 循环将一直进行下去。

        方法区用于存放class的相关信息, 如类名,访问修饰符、常量池、字段描述,方法描述等。
        当前的很多主流框架,如Spring. Hibemate, 在对类进行增强时,都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载人内存。另外,JVM上的动态语言(例如Groovy等)通常都会持续创建类来实现请言的动态性。
        举例:借助CGLib使方法区出现内存溢出异常

    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodProxy;
     
    /**
     * VM Args:- XX:PermSize=10m -XX:MaxPermSize=10m
     */
    public class JavaMethodAreaOOM {
           public static void main(String[] args) {
                 while (true){
                      Enhancer enhancer = new Enhancer();
                      enhancer.setSuperclass(OOMObject.class);
                      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();
                }
          }
    
          static class OOMObject{
          }
    }
    
    
    运行结果:
    outOfMemoryError:permgem space


        方法区溢出产生的情况,使用了CGLih字节码增强和动态语言,大量ISP或动态产生JSP文件的应用(JSP 第一次运行时需要编译为Java类)、基于OSGI的应用(即使是同一个类文件,被不同的加载8加载也会视为不同的类)。

    本机直接内存溢出

        DircMemory(直接存储器)容量可通过-XX:MaxDirectMemorySize 指定,如果不指定,则默认与Java堆最大值(-Xmx指定)一样。
        Unsafe类的getUnsafcO方法限制了只有引导类加载器才会返回实例,也就是设计者希望只有r.jar中的类才能使用Unsafe的功能。
        真正中请分配内存的法是unsafe alocateMemory0。
        举例:使用unsafe分配本机内存

    public class DirectMemoryOOM {
        private static final int _1MB = 1024 * 1024;
    
        public static void main(String[] args) throws Exception {
            Field unsafeField = Unsafe.class.getDeclaredFields()[0];
            unsafeField.setAccessible(true);
            Unsafe unsafe = (Unsafe) unsafeField.get(null);
            while (true) {
                unsafe.allocateMemory(_1MB);
            }
        }
    }
    
    
    运行结果:
    Exception in thread "main" java.lang.OutOfMemoryError
        at sun.misc.Unsafe.allocateMemory(Native Method)
        at oom.DirectMemoryOOM.main(DirectMemoryOOM.java:23)
  • 相关阅读:
    C++笔记(2018/2/6)
    2017级面向对象程序设计寒假作业1
    谁是你的潜在朋友
    A1095 Cars on Campus (30)(30 分)
    A1083 List Grades (25)(25 分)
    A1075 PAT Judge (25)(25 分)
    A1012 The Best Rank (25)(25 分)
    1009 说反话 (20)(20 分)
    A1055 The World's Richest(25 分)
    A1025 PAT Ranking (25)(25 分)
  • 原文地址:https://www.cnblogs.com/silentdoer/p/12750263.html
Copyright © 2011-2022 走看看