java内存溢出 原因与排查方法

1、 内存溢出的原因是什么?

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

一)是否应用中的类中和引用变量过多使用了Static修饰 如public staitc Student s;在类中的属性中使用 static修饰的最好只用基本类型或字符串。如public static int i = 0; //public static String str;

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

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

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

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

六)检查是否使用了“非字面量字符串进行+”的操作。因为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();     }
} 

2、栈溢出的原因

一)、是否有递归调用

二)、是否有大量循环或死循环

三)、全局变量是否过多

四)、 数组、List、map数据是否过大

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

后续持续更新 请看到的及时补充到评论区……

下面是摘自掘金中的一篇文章,在项目过程中或多或少遇到过,由于本人不想再一一做测试用例,就摘录过来了,最后附带地址链接 ,可以方便大家去看原文(尊重原著)

JVM系列之实战内存溢出异常

实战内存溢出异常

大家好,相信大部分Javaer在code时经常会遇到本地代码运行正常,但在生产环境偶尔会莫名其妙的报一些关于内存的异常,StackOverFlowError,OutOfMemoryError异常是最常见的。今天就基于上篇文章JVM系列之Java内存结构详解讲解的各个内存区域重点实战分析下内存溢出的情况。在此之前,我还是想多余累赘一些其他关于对象的问题,具体内容如下:

文章结构

对象的创建过程
对象的内存布局
对象的访问定位
实战内存异常

1 . 对象的创建过程

关于对象的创建,第一反应是new关键字,那么本文就主要讲解new关键字创建对象的过程。

Student stu =new Student("张三","18");

就拿上面这句代码来说,虚拟机首先会去检查Student这个类有没有被加载,如果没有,首先去加载这个类到方法区,然后根据加载的Class类对象创建stu实例对象,需要注意的是,stu对象所需的内存大小在Student类加载完成后便可完全确定。内存分配完成后,虚拟机需要将分配到的内存空间的实例数据部分初始化为零值,这也就是为什么我们在编写Java代码时创建一个变量不需要初始化。紧接着,虚拟机会对对象的对象头进行必要的设置,如这个对象属于哪个类,如何找到类的元数据(Class对象),对象的锁信息,GC分代年龄等。设置完对象头信息后,调用类的构造函数。
其实讲实话,虚拟机创建对象的过程远不止这么简单,我这里只是把大致的脉络讲解了一下,方便大家理解。

2 . 对象的内存布局

刚刚提到的实例数据,对象头,有些小伙伴也许有点陌生,这一小节就详细讲解一下对象的内存布局,对象创建完成后大致可以分为以下几个部分:

对象头
实例数据
对齐填充

对象头: 对象头中包含了对象运行时一些必要的信息,如GC分代信息,锁信息,哈希码,指向Class类元信息的指针等,其中对Javaer比较有用的是锁信息与指向Class对象的指针,关于锁信息,后期有机会讲解并发编程JUC时再扩展,关于指向Class对象的指针其实很好理解。比如上面那个Student的例子,当我们拿到stu对象时,调用Class stuClass=stu.getClass();的时候,其实就是根据这个指针去拿到了stu对象所属的Student类在方法区存放的Class类对象。虽然说的有点拗口,但这句话我反复琢磨了好几遍,应该是说清楚了。

实例数据:实例数据部分是对象真正存储的有效信息,就是程序代码中所定义的各种类型的字段内容。

对齐填充:虚拟机规范要求对象大小必须是8字节的整数倍。对齐填充其实就是来补全对象大小的。

3 . 对象的访问定位

谈到对象的访问,还拿上面学生的例子来说,当我们拿到stu对象时,直接调用stu.getName();时,其实就完成了对对象的访问。但这里要累赘说一下的是,stu虽然通常被认为是一个对象,其实准确来说是不准确的,stu只是一个变量,变量里存储的是指向对象的指针,(如果干过C或者C++的小伙伴应该比较清楚指针这个概念),当我们调用stu.getName()时,虚拟机会根据指针找到堆里面的对象然后拿到实例数据name.需要注意的是,当我们调用stu.getClass()时,虚拟机会首先根据stu指针定位到堆里面的对象,然后根据对象头里面存储的指向Class类元信息的指针再次到方法区拿到Class对象,进行了两次指针寻找。具体讲解图如下:

4 .实战内存异常

内存异常是我们工作当中经常会遇到问题,但如果仅仅会通过加大内存参数来解决问题显然是不够的,应该通过一定的手段定位问题,到底是因为参数问题,还是程序问题(无限创建,内存泄露)。定位问题后才能采取合适的解决方案,而不是一内存溢出就查找相关参数加大。

概念:
内存泄露:代码中的某个对象本应该被虚拟机回收,但因为拥有GCRoot引用而没有被回收。关于GCRoot概念,下一篇文章讲解。
内存溢出: 虚拟机由于堆中拥有太多不可回收对象没有回收,导致无法继续创建新对象。

在分析问题之前先给大家讲一讲排查内存溢出问题的方法,内存溢出时JVM虚拟机会退出,那么我们怎么知道JVM运行时的各种信息呢,Dump机制会帮助我们,可以通过加上VM参数-XX:+HeapDumpOnOutOfMemoryError让虚拟机在出现内存溢出异常时生成dump文件,然后通过外部工具(作者使用的是VisualVM)来具体分析异常的原因。

下面从以下几个方面来配合代码实战演示内存溢出及如何定位:

Java堆内存异常
Java栈内存异常
方法区内存异常

Java堆内存异常

/** VM Args: //这两个参数保证了堆中的可分配内存固定为20M -Xms20m -Xmx20m
//文件生成的位置,则生成在桌面的一个目录 -XX:+HeapDumpOnOutOfMemoryError
//文件生成的位置,则生成在桌面的一个目录
//文件生成的位置,则生成在桌面的一个目录 -XX:HeapDumpPath=/Users/zdy/Desktop/dump/ */
public class HeapOOM {//创建一个内部类用于创建对象使用static class OOMObject {}public static void main(String[] args) {List<OOMObject> list = new ArrayList<OOMObject>();//无限创建对象,在堆中while (true) {list.add(new OOMObject());}}
}

Run起来代码后爆出异常如下:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to /Users/zdy/Desktop/dump/java_pid1099.hprof ...

可以看到生成了dump文件到指定目录。并且爆出了OutOfMemoryError,还告诉了你是哪一片区域出的问题:heap space

打开VisualVM工具导入对应的heapDump文件(如何使用请读者自行查阅相关资料),相应的说明见图:

分析dump文件后,我们可以知道,OOMObject这个类创建了810326个实例。所以它能不溢出吗?接下来就在代码里找这个类在哪new的。排查问题。(我们的样例代码就不用排查了,While循环太凶猛了)

Java栈内存异常

老实说,在栈中出现异常(StackOverFlowError)的概率很小,小到和去苹果专卖店买手机,买回来后发现是Android系统的概率是一样的。因为作者确实没有在生产环境中遇到过,除了自己作死写样例代码测试。先说一下异常出现的情况,前面讲到过,方法调用的过程就是方法帧进虚拟机栈和出虚拟机栈的过程,那么有两种情况可以导致StackOverFlowError,当一个方法帧(比如需要2M内存)进入到虚拟机栈(比如还剩下1M内存)的时候,就会报出StackOverFlow.这里先说一个概念,栈深度:指目前虚拟机栈中没有出栈的方法帧。虚拟机栈容量通过参数-Xss来控制,下面通过一段代码,把栈容量人为的调小一点,然后通过递归调用触发异常。

/** * VM Args: //设置栈容量为160K,默认1M -Xss160k */
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.println("stack length:" + oom.stackLength);throw e;}}
}

结果如下:
stack length:751
Exception in thread "main" java.lang.StackOverflowError

可以看到,递归调用了751次,栈容量不够用了。
默认的栈容量在正常的方法调用时,栈深度可以达到1000-2000深度,所以,一般的递归是可以承受的住的。如果你的代码出现了StackOverflowError,首先检查代码,而不是改参数。

这里顺带提一下,很多人在做多线程开发时,当创建很多线程时,容易出现OOM(OutOfMemoryError),

这时可以通过具体情况,减少最大堆容量,或者栈容量来解决问题,这是为什么呢。请看下面的公式:

线程数 * (最大栈容量) + 最大堆值 + 其他内存(忽略不计或者一般不改动) = 机器最大内存

当线程数比较多时,且无法通过业务上削减线程数,那么再不换机器的情况下,你只能把最大栈容量设置小一点,或者把最大堆值设置小一点。

方法区内存异常

写到这里时,作者本来想写一个无限创建动态代理对象的例子来演示方法区溢出,避开谈论JDK7与JDK8的内存区域变更的过渡,但细想一想,还是把这一块从始致终的说清楚。在上一篇文章中JVM系列之Java内存结构详解讲到方法区时提到,JDK7环境下方法区包括了(运行时常量池),其实这么说是不准确的。因为从JDK7开始,HotSpot团队就想到开始去"永久代",大家首先明确一个概念,方法区和"永久代"(PermGen space)是两个概念,方法区是JVM虚拟机规范,任何虚拟机实现(J9等)都不能少这个区间,而"永久代"只是HotSpot对方法区的一个实现。为了把知识点列清楚,我还是才用列表的形式:

JDK7之前(包括JDK7)拥有"永久代"(PermGen space),用来实现方法区。但在JDK7中已经逐渐在实现中把永久代中把很多东西移了出来,比如:符号引用(Symbols)转移到了native heap,运行时常量池(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。
所以这就是为什么我说上一篇文章中说方法区中包含运行时常量池是不正确的,因为已经移动到了java heap;
在JDK7之前(包括7)可以通过-XX:PermSize -XX:MaxPermSize来控制永久代的大小。
JDK8正式去除"永久代",换成Metaspace(元空间)作为JVM虚拟机规范中方法区的实现。
元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但仍可以通过参数控制:-XX:MetaspaceSize与-XX:MaxMetaspaceSize来控制大小。
下面作者还是通过一段代码,来不停的创建Class对象,在JDK8中可以看到metaSpace内存溢出:

/** 作者准备在JDK8下测试方法区,所以设置了Metaspace的大小为固定的8M -XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m */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() {public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {return proxy.invokeSuper(obj, args);}});//无限创建动态代理,生成Class对象enhancer.create();}}static class OOMObject {}
}

在JDK8的环境下将报出异常:
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
这是因为在调用CGLib的创建代理时会生成动态代理类,即Class对象到Metaspace,所以While一下就出异常了。
提醒一下:虽然我们日常叫"堆Dump",但是dump技术不仅仅是对于"堆"区域才有效,而是针对OOM的,也就是说不管什么区域,凡是能够报出OOM错误的,都可以使用dump技术生成dump文件来分析。

在经常动态生成大量Class的应用中,需要特别注意类的回收状况,这类场景除了例子中的CGLib技术,常见的还有,大量JSP,反射,OSGI等。需要特别注意,当出现此类异常,应该知道是哪里出了问题,然后看是调整参数,还是在代码层面优化。

附加-直接内存异常

直接内存异常非常少见,而且机制很特殊,因为直接内存不是直接向操作系统分配内存,而且通过计算得到的内存不够而手动抛出异常,所以当你发现你的dump文件很小,而且没有明显异常,只是告诉你OOM,你就可以考虑下你代码里面是不是直接或者间接使用了NIO而导致直接内存溢出。

原文地址https://my.oschina.net/u/2401092/blog/1621850

java内存溢出 栈溢出的原因与排查方法相关推荐

  1. 【Java面试】Java 内存溢出 栈溢出的原因与排查方法

    1. 内存溢出的原因是什么? 内存溢出是由于没被引用的对象(垃圾)过多造成JVM没有及时回收,造成的内存溢出.如果出现这种现象可行代码排查: 一)是否App中的类中和引用变量过多使用了Static修饰 ...

  2. linux 内存溢出排查_java 内存溢出 栈溢出的原因与排查方法

    1. 内存溢出的原因是什么? 内存溢出是由于没被引用的对象(垃圾)过多造成JVM没有及时回收,造成的内存溢出.如果出现这种现象可行代码排查: 一)是否应用中的类中和引用变量过多使用了Static修饰 ...

  3. java内存溢出模拟_模拟实战排查堆内存溢出(java.lang.OutOfMemoryError: Java heap space)问题...

    前言: 模拟实战中排查堆内存溢出(java.lang.OutOfMemoryError: Java heap space)的问题. 堆内存溢出的原因:一般都是创建了大量的对象,这些对象一直被引用着,无 ...

  4. jvm 内存溢出的多种原因及优化方法

    让我们看一下我们日常在开发过程中接触内存溢出的异常: Exception in thread "main" [Full GCjava.lang.OutOfMemoryError: ...

  5. Java 内存溢出(一)原因、复现、排查

    目录 一.内存溢出原因 二.内存溢出实例 1.堆溢出 2.虚拟机栈和本地方法栈溢出 3.方法区和运行时常量池溢出 4.本机直接内存溢出 三.内存溢出排查 内存溢出: 是指应用系统中存在无法回收的内存或 ...

  6. Java内存溢出问题排查分析

    目录 前言 一.MAT(Memory Analyzer Tool) 二.软件初识 三.捕获dump文件 1.主动方式 2.被动方式 四.分析dump文件 总结 前言 项目运行过程中,我们可能会遇到Ja ...

  7. 【Java内存溢出排查】gc监测以及内存突增问题排查

    前情提要 文档:[Java内存溢出排查]测试环境服务器挂... 链接:http://note.youdao.com/noteshare?id=783e7ec89950f4167867ef3ef3347 ...

  8. java 内存 溢出_java内存溢出的几种原因和解决办法是什么?

    java内存溢出的几种原因和解决办法是什么? java内存溢出的几种原因和解决办法是: 第一类内存溢出,也是大家认为最多,第一反应认为是的内存溢出,就是堆栈溢出: 那什么样的情况就是堆栈溢出呢?当你看 ...

  9. java内存溢出原因及解决_java内存溢出的原因和解决方法

    java内存溢出的原因和解决方法 发布时间:2020-06-15 17:57:39 来源:亿速云 阅读:85 作者:元一 内存溢出含义: 内存溢出(out of memory)通俗理解就是内存不够,通 ...

最新文章

  1. IDEA中maven的命令及作用
  2. 数据预处理|关于标准化和归一化的一切
  3. 管家婆SQL SERVER数据库“可能发生了架构损坏。请运行DBCC CHECKCATALOG”修复
  4. Docker 容器部署 Consul 集群
  5. Nginx日志配置详解
  6. 【华为云技术分享】《跟唐老师学习云网络》 - Kubernetes网络实现
  7. bzoj3202:[Sdoi2013]项链
  8. 虚拟机可以识别usb无线网卡,但一直提示设备正在运行中--解决方法
  9. 生活的解释 —— 生物篇
  10. 【渝粤题库】陕西师范大学209015公共经济学作业(高起本、专升本)
  11. powerbuilder防止反编译: pbkiller无法解析的部分公布
  12. 声纹识别demo_科学网—声纹识别、说话人识别软件,SPEAKER v0.1 - 石自强的博文...
  13. Android zip文件压缩与解压
  14. MySQL多个关键词检索字段
  15. 生成对抗网络(GAN)生成MNIST数据
  16. 论文解读 | CenterNet:Keypoint Triplets for Object Detection
  17. formula的java用法_java-@Formula无法在休眠状态下与对象一起使用
  18. python读取页眉页脚,python批量替换页眉页脚
  19. 支持英特尔9242的服务器,宝德2U双子星服务器PR2725TP2
  20. (CSA 共识评估调查问卷)CSA Consensus Assessments Initiative Questionnaire

热门文章

  1. 无障碍访问的快捷方式
  2. 如何读/写论文???
  3. cmos-sensor的原理介绍
  4. 软件工程--牙科诊所--需求分析
  5. 为什么大电容用于低频通路,小电容用于高频电路
  6. 霍尼韦尔携手天地互联为中国航司提供机上高速互联解决方案;思享无限正式开启数字艺术品试点计划 | 全球TMT...
  7. Jpa使用详解(Hibernate)
  8. 神经系统图 基本结构图,神经系统分类和组成图
  9. 【CRMEB 标准版】V4.0.31常见问题处理办法 问题修复汇总
  10. MATLAB绘制雷达图/蜘蛛图