目录

  • 一、内存溢出原因
  • 二、内存溢出实例
    • 1、堆溢出
    • 2.虚拟机栈和本地方法栈溢出
    • 3.方法区和运行时常量池溢出
    • 4.本机直接内存溢出
  • 三、内存溢出排查

内存溢出: 是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。这篇文章整理自《深入理解 java 虚拟机》。

一、内存溢出原因

内存溢出就是内存不够,引起内存溢出的原因有很多种,常见的有以下几种:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得 JVM 不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的 BUG;
  5. 启动参数内存值设定的过小。

当然实际情况中内存溢出的原因就太多了。

以上的图是基于 java7 来描述的,从上面这张图我们能够得到如下信息:java 虚拟机把内存分为 5 个模块。

(1)程序计数器:程序计数器是线程私有的,主要作用是通过改变这个计数器的值来选取下一条需要执行的字节码指令。既然每个线程都有一个,那么这些线程的计数器是互不影响的,也不会抛出任何异常。

(2)虚拟机栈和本地方法栈:虚拟机栈描述的是 java 方法执行的内存模型,每个方法在执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。本地方法栈与虚拟机栈的区别是,虚拟机栈为虚拟机执行 java 方法服务,而本地方法栈则为虚拟机提供 native 方法服务。

在单线程的操作中,无论是由于栈帧太大,还是虚拟机栈空间太小,当栈空间无法分配时,虚拟机抛出的都是 StackOverflowError 异常,而不会得到 OutOfMemoryError 异常。而在多线程环境下,则会抛出 OutOfMemoryError 异常。

(3)java 堆和方法区:java 堆区主要存放对象实例和数组等,方法区保存类信息、常量、静态变量等等。运行时常量池也是方法区的一部分。这两块区域是线程共享的区域,只会抛出 OutOfMemoryError。

二、内存溢出实例

1、堆溢出

既然堆是存放实例对象的,那我们就无限创建实例对象。这样堆区迟早会满。

执行时需要设置VM参数:-Xmx10M,限制最大堆内存为10M

import java.util.ArrayList;
import java.util.List;public class HeapOOM {static class User {}public static void main(String[] args) {List<User> list = new ArrayList<>();while (true) {list.add(new User());}}
}

异常内容:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:3210)at java.util.Arrays.copyOf(Arrays.java:3181)at java.util.ArrayList.grow(ArrayList.java:261)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)at java.util.ArrayList.add(ArrayList.java:458)at com.demo.HeapOOM.main(HeapOOM.java:6)

因为我提前设置了堆区内存限制,所以无限创建就会抛出异常。

2.虚拟机栈和本地方法栈溢出

Java 虚拟机规范中描述了两种异常:

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常。

第一种我们只需要使用方法递归调用即可模拟:

public class StackOutOfMemoryError {public static void main(String[] args) {test();}private static void test() {System.out.println("StackOverflowError 异常测试");test();}
}

异常内容:

Exception in thread "main" java.lang.StackOverflowErrorat sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)at java.io.PrintStream.write(PrintStream.java:526)at java.io.PrintStream.print(PrintStream.java:669)at java.io.PrintStream.println(PrintStream.java:806)at com.demo.StackOutOfMemoryError.test(StackOutOfMemoryError.java:6)

第二种也可以递归调用模拟,但是使用的是类直接调用:

public class JavaVMStackSOF {private int stackLength = 1;public void stackLeak() {stackLength++;stackLeak();}public static void main(String[] args) {JavaVMStackSOF oom = new JavaVMStackSOF();oom.stackLeak();}
}

异常内容:

Exception in thread "main" java.lang.StackOverflowErrorat com.demo.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:5)

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

执行时需要设置VM参数:-Xmx10M,限制最大堆内存为10M

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class JavaMethodAreaOOM {private static class User {}public static void main(String[] args) {while (true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(User.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method,Object[] args, MethodProxy proxy) throws Throwable {return proxy.invokeSuper(obj, args);}});enhancer.create();}}
}

异常内容:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceededat java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:68)at java.lang.StringBuffer.<init>(StringBuffer.java:116)at org.objectweb.asm.Type.getDescriptor(Unknown Source)at net.sf.cglib.core.ClassEmitter.declare_field(ClassEmitter.java:193)at net.sf.cglib.proxy.MethodInterceptorGenerator.generate(MethodInterceptorGenerator.java:94)at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:994)at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498)at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)at com.demo.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:22)

4.本机直接内存溢出

执行时需要设置VM参数:-Xmx10M,限制最大堆内存为10M

import sun.misc.Unsafe;import java.lang.reflect.Field;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.OutOfMemoryErrorat sun.misc.Unsafe.allocateMemory(Native Method)at com.demo.DirectMemoryOOM.main(DirectMemoryOOM.java:12)

DirectMemory 容量可通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认与 Java 堆最大值(-Xmx 指定)一样。

三、内存溢出排查

排查最主要的就是检查代码,而且内存溢出往往都是代码的问题。当然以下几点都是需要注意的:

(1)内存中加载的数据量过于庞大,如一次性从数据库取出过多数据;

(2)集合类中有对对象的引用,使用完后未清空,是的 JVM 不能回收;

(3)代码中存在死循环或循环产生过多重复的对象实体;

(4)使用的第三方软件中的 BUG;

(5)启动参数内存值设定的过小。

内存溢出解决方法:

方法一:修改 JVM 启动参数,直接增加内存,暂时解决问题;

方法二:检查错误日志,定位可能发生内存溢出的位置,优化代码;

方法三:如果设置了内存参数 -XX:+HeapDumpOnOutOfMemoryError,当内存溢出时会产生 .hprof 文件(记录了 Java 进程在某个时间内的快照),可以通过 MAT 工具进行分析,优化代码。

整理完毕,完结撒花~

参考地址:

1.java内存溢出,https://blog.csdn.net/u014401141/article/details/122825443

Java 内存溢出(一)原因、复现、排查相关推荐

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

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

  2. 常见的Java内存溢出情况和实例

    文章目录 内存溢出的原因 栈溢出 栈溢出的第1种情况,死递归,抛出StackOverflowError 栈溢出的第2种情况,线程太多,抛出OutOfMemoryError 堆溢出 1.初始对象太大,超 ...

  3. java内存溢出 栈溢出的原因与排查方法

    java内存溢出 原因与排查方法 1. 内存溢出的原因是什么? 内存溢出是由于没被引用的对象(垃圾)过多造成JVM没有及时回收,导致剩余的内存不够用,造成的内存溢出.如果出现这种现象可行代码排查: 一 ...

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

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

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

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

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

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

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

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

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

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

  9. java面试-内存溢出的原因及解决办法

    内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存. 引起内存溢出的原因有很多种,常见的有以下几种: 1.内存中加载的数据量过于庞大,如一 ...

最新文章

  1. [ucgui] 对话框7——按钮触发与模式窗口
  2. Ubuntu 16.04 amd64 bond mode6 配置
  3. C++类和对象学习总结
  4. 图像识别 标注(annotation)的反向优化策略
  5. android 查看gpio状态_GPIO子系统重要概念
  6. plsql中的if判断
  7. 2018.4.23 数据结构
  8. 微博广告推荐策略工程架构体系演进
  9. 【免费分享】收集整理的117套各类微信小程序模板源码分享
  10. kX3552常用插件集
  11. 74cms v6.0.48模版注入+文件包含getshell复现
  12. 流光容易把人抛,红了樱桃,绿了芭蕉
  13. 王者荣耀s16服务器维护,王者荣耀:S16单排环境依旧差劲 五大原因戳到很多人的痛点...
  14. #华为云#听从你心,无问西东
  15. l33t-hoster .htaccess \x00注释putenv绕过disable_function计算c代码
  16. 嵌入式开发——Linux操作系统
  17. android中点击头像放大,Android头像下拉缩放动效
  18. FairyGUI增益BUFF数值改变的显示
  19. Vue源码翻译之渲染逻辑链
  20. backtrader和vnpy哪个更好用?

热门文章

  1. java 构造函数和super
  2. 域控制器「自研」还有戏吗?他们为何「跳出」华为MDC生态
  3. Clickhouse的数据存储原理、二进制文件内容分析与索引详解
  4. 管理类联考笔试还是计算机考,干货来了!管理类联考笔试考什么?大纲及卷面解读。...
  5. 什么是显函数,什么是隐函数
  6. 带你看看GeoJSON是什么!
  7. 库卡KUKA-Arrays-数组变量-系统变量
  8. 实用成都幼儿园设计装修案例效果图推荐
  9. python自动点击脚本_用Python实现鼠标自动点击
  10. MATLAB与C/C++混合编程之MATLAB调用C程序