zoukankan      html  css  js  c++  java
  • 浅析JVM堆溢出和栈溢出以及其产生可能原因的排查要点

    一、JVM 堆溢出

      在 jvm 运行 java 程序时,如果程序运行所需要的内存大于系统的堆最大内存(-Xmx),就会出现堆溢出问题。创建对象时如果没有可以分配的堆内存,JVM就会抛出OutOfMemoryError:java heap space异常。

    // 执行该段代码需要大于10m内存空间
    public class HeadOverflow {
        public static void main(String[] args) {
            List<Object> listObj = new ArrayList<Object>();
            for(int i=0; i<10; i++){
                Byte[] bytes = new Byte[1*1024*1024];
                listObj.add(bytes);
            }
            System.out.println("添加success");
        }
    }
     
    // 设置该程序的jvm参数信息
    -Xms1m -Xmx10m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
    初始堆内存和最大可以堆内存 Gc详细日志信息
    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to java_pid2464.hprof ...
    Heap dump file created [16991068 bytes in 0.047 secs]
    
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at com.ghs.test.OOMTest.main(OOMTest.java:16)

      在正式项目部署环境程序默认读取的是系统的内存,一般设置程序的堆初始内存(-Xms) == 堆最大可用内存(-Xmx)。

    二、JVM 栈溢出

    1、栈溢出介绍:栈空间不足时,需要分下面两种情况处理:

    (1)线程请求的栈深度大于虚拟机允许的最大深度   -   StackOverflowError

    (2)虚拟机在扩展栈深度时,无法申请到足够的内存空间  -   OutOfMemoryError

      理解:每次方法调用都会有一个栈帧压入虚拟机栈,操作系统给JVM分配的内存是有限的,JVM分配给“虚拟机栈”的内存是有限的。如果方法调用过多,导致虚拟机栈满了就会溢出。这里栈深度就是指栈帧的数量。

    2、案例

    // 循环递归调用,一直达到jvm的最大深度
    public class StackOverflow {
         private static int count;
         public static void count(){
            try {
                 count++;
                 count(); 
            } catch (Throwable e) {
                System.out.println("最大深度:"+count);
                e.printStackTrace();
            }
         }
         public static void main(String[] args) {
             count();
        }
    }

    3、调整 jvm 栈大小:设置-Xss5m设置最大调用深度后调用

      每个计算机都会有一个极限最大调用深度,避免递归在代码中无限循环。局部变量表内容越多,栈帧越大,栈深度越小。

      这里有一篇文章讲的是各自溢出的问题,可以看看:《写代码实现堆溢出、栈溢出、永久代溢出、直接内存溢出 - https://blog.csdn.net/u011983531/article/details/63250882》

      (1)栈内存溢出:程序所要求的栈深度过大。

      (2)堆内存溢出: 分清内存泄露还是 内存容量不足。泄露则看对象如何被 GC Root 引用,不足则通过调大-Xms,-Xmx参数。

      (3)永久代溢出:Class对象未被释放,Class对象占用信息过多,有过多的Class对象。

      (4)直接内存溢出:系统哪些地方会使用直接内存。

    三、内存溢出的原因是什么

    1、内存溢出与内存泄漏:

      内存溢出:申请内存空间,超出最大堆内存空间。

      内存泄露:其实包含了内存溢出,堆内存空间被无用对象占用没有及时释放,导致占用内存,最终导致内存泄露。

    2、内存溢出的可能原因排查

      内存溢出是由于没被引用的对象(垃圾)过多造成JVM没有及时回收,造成的内存溢出。如果出现这种现象可进行代码排查:

    (1)是否应用中的类中和引用变量过多使用了Static修饰,如 public staitc Students;

      在类中的属性中使用 static 修饰的最好只用基本类型或字符串。如:public static int i = 0;  public static String str;

    (2)是否 应用 中使用了大量的递归或无限递归(递归中用到了大量的建新的对象

    (3)是否App中使用了大量循环或死循环(循环中用到了大量的新建的对象)

    (4)检查 应用 中是否使用了向数据库查询所有记录的方法。即一次性全部查询的方法,如果数据量超过10万多条了,就可能会造成内存溢出。所以在查询时应采用“分页查询”。

    (5)检查是否有数组,List,Map中存放的是对象的引用而不是对象,因为这些引用会让对应的对象不能被释放,会大量存储在内存中。

    (6)检查是否使用了“非字面量字符串进行+”的操作

      因为String类的内容是不可变的,每次运行"+"就会产生新的对象,如果过多会造成新String对象过多,从而导致JVM没有及时回收而出现内存溢出

    //
    String s1 = "My name";
    String s2 = "is";
    String s3 = "xuwei";
    String str = s1 + s2 + s3 +.........;  // 这是会容易造成内存溢出的
    
    // 但是String str =  "My name" + " is " + " xuwei" + " nice " + " to " + " meet you"; 
    // 这种就不会造成内存溢出。因为这是”字面量字符串“,在运行"+"时就会在编译期间运行好。不会按照JVM来执行的。

      在使用 String、StringBuffer、StringBuilder 时,如果是字面量字符串进行"+"时,应选用String性能更好;如果是String类进行"+"时,在不考虑线程安全时,应选用 StringBuilder 性能更好。

      再比如下面这些错误的示例:

        public class Test {  
            public void testHeap(){  
                for(;;){  //死循环一直创建对象,堆溢出
                      ArrayList list = new ArrayList (2000);  
                  }  
            }  
            int num=1;  
            public void testStack(){  //无出口的递归调用,栈溢出
                num++;  
                this.testStack();  
             }  
            public static void main(String[] args){  
                Test  t  = new Test ();  
                t.testHeap();  
                t.testStack();     
            }  
        } 

    3、栈溢出的原因

    (1)是否有递归调用

    (2)是否有大量循环或死循环

    (3)全局变量是否过多

    (4)数组、List、map数据是否过大

    (5)使用DDMS工具进行查找大概出现栈溢出的位置

  • 相关阅读:
    0505.Net基础班第十四天(winform基础)
    0505.Net基础班第十三天(面向对象多态)
    Z-index
    div的padding和margin
    隐藏div,文本框角圆滑,消除外边框
    页面加载完成之后运行方法里的内容,隐藏标签,判断字符串里面是否包含某个字符
    CSS命令
    漂浮
    电子时钟
    用二维数组存数据(学科成绩、总分以及平均值)
  • 原文地址:https://www.cnblogs.com/goloving/p/15267760.html
Copyright © 2011-2022 走看看