1. 栈溢出(StackOverflowError)
  2. 堆溢出(OutOfMemoryError:Java heap space)
  3. 永久代溢出(OutOfMemoryError: PermGen space)
  4. 直接内存溢出

一、堆溢出

创建对象时如果没有可以分配的堆内存,JVM就会抛出OutOfMemoryError:java heap space异常。

堆溢出实例:

/*** VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError*/
public static void main(String[] args) {List<byte[]> list = new ArrayList<>();int i=0;while(true){list.add(new byte[5*1024*1024]);System.out.println("分配次数:"+(++i));}
}运行结果:
分配次数:1
分配次数:2
分配次数:3java.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 spaceat com.ghs.test.OOMTest.main(OOMTest.java:16)

附:dump文件会在项目的根目录下生成

从上面的例子我们可以看出,在进行第4次内存分配时,发生了内存溢出。

二、栈溢出

栈空间不足时,需要分下面两种情况处理:

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

附:当前大部分的虚拟机栈都是可动态扩展的。

1、栈空间不足——StackOverflowError实例

public class StackSOFTest {int depth = 0;public void sofMethod(){depth ++ ;sofMethod();}public static void main(String[] args) {StackSOFTest test = null;try {test = new StackSOFTest();test.sofMethod();} finally {System.out.println("递归次数:"+test.depth);}}
}执行结果:
递归次数:982
Exception in thread "main" java.lang.StackOverflowErrorat com.ghs.test.StackSOFTest.sofMethod(StackSOFTest.java:8)at com.ghs.test.StackSOFTest.sofMethod(StackSOFTest.java:9)at com.ghs.test.StackSOFTest.sofMethod(StackSOFTest.java:9)
……后续堆栈信息省略

我们可以看到,sofMethod()方法递归调用了982次后,出现了StackOverflowError。

2、栈空间不足——OutOfMemberError实例
单线程情况下,不论是栈帧太大还是虚拟机栈容量太小,都会抛出StackOverflowError,导致单线程情境下模拟栈内存溢出不是很容易,不过通过不断的建立线程倒是可以产生内存溢出异常。

public class StackOOMTest {public static void main(String[] args) {StackOOMTest test = new StackOOMTest();test.oomMethod();}public void oomMethod(){while(true){new Thread(new Runnable() {@Overridepublic void run() {loopMethod();}}).start();;}}private void loopMethod(){while(true){}}
}运行结果:
……操作系统直接挂掉了

如果哪位大神能够成功模拟,还望指点一二。

三、永久代溢出

永久代溢出可以分为两种情况,第一种是常量池溢出,第二种是方法区溢出。

1、永久代溢出——常量池溢出
要模拟常量池溢出,可以使用String对象的intern()方法。如果常量池包含一个此String对象的字符串,就返回代表这个字符串的String对象,否则将String对象包含的字符串添加到常量池中。

public class ConstantPoolOOMTest {/*** VM Args:-XX:PermSize=10m -XX:MaxPermSize=10m* @param args*/public static void main(String[] args) {List<String> list = new ArrayList<>();int i=1;try {while(true){list.add(UUID.randomUUID().toString().intern());i++;}} finally {System.out.println("运行次数:"+i);}}
}运行结果:
……比较尴尬的是,通过intern,始终无法模拟出常量池溢出,我的猜想是JDK7对常量池做了优化。
如果哪位大神成功模拟出来了,还望指点一二。

找了好久,终于弄清楚了使用string.intern()方法无法模拟常量池溢出的原因。

因为在JDK1.7中,当常量池中没有该字符串时,JDK7的intern()方法的实现不再是在常量池中创建与此String内容相同的字符串,而改为在常量池中记录Java Heap中首次出现的该字符串的引用,并返回该引用。
简单来说,就是对象实际存储在堆上面,所以,让上面的代码一直执行下去,最终会产生堆内存溢出。
下面我将堆内存设置为:-Xms5m -Xmx5m,执行上面的代码,运行结果如下:

运行次数:58162
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.lang.Long.toUnsignedString(Unknown Source)at java.lang.Long.toHexString(Unknown Source)at java.util.UUID.digits(Unknown Source)at java.util.UUID.toString(Unknown Source)at com.ghs.test.ConstantPoolOOMTest.main(ConstantPoolOOMTest.java:18)

2、永久代溢出——方法区溢出
方法区存放Class的相关信息,下面借助CGLib直接操作字节码,生成大量的动态类。

public class MethodAreaOOMTest {public static void main(String[] args) {int i=0;try {while(true){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OOMObject.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();i++;}} finally{System.out.println("运行次数:"+i);}}static class OOMObject{}
}运行结果:运行次数:56
Exception in thread "main"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"

虽然出现了异常,但是打印的堆栈信息似乎并不是我们想要的……

四、直接内存溢出

DirectMemory可以通过-XX:MaxDirectMemorySize指定,如果不指定,默认与Java堆的最大值(-Xmx指定)一样。
NIO会使用到直接内存,你可以通过NIO来模拟,在下面的例子中,跳过NIO,直接使用UnSafe来分配直接内存。

public class DirectMemoryOOMTest {/*** VM Args:-Xms20m -Xmx20m -XX:MaxDirectMemorySize=10m* @param args*/public static void main(String[] args) {int i=0;try {Field field = Unsafe.class.getDeclaredFields()[0];field.setAccessible(true);Unsafe unsafe = (Unsafe) field.get(null);while(true){unsafe.allocateMemory(1024*1024);i++;}} catch (Exception e) {e.printStackTrace();}finally {System.out.println("分配次数:"+i);}}
}运行结果:
Exception in thread "main" java.lang.OutOfMemoryErrorat sun.misc.Unsafe.allocateMemory(Native Method)at com.ghs.test.DirectMemoryOOMTest.main(DirectMemoryOOMTest.java:20)
分配次数:27953

总结:
栈内存溢出:程序所要求的栈深度过大。
堆内存溢出: 分清内存泄露还是 内存容量不足。泄露则看对象如何被 GC Root 引用,不足则通过调大-Xms,-Xmx参数。
永久代溢出:Class对象未被释放,Class对象占用信息过多,有过多的Class对象。
直接内存溢出:系统哪些地方会使用直接内存。

最后,送大家一句话:觉知此事要躬行。
很多时候,我们看别人提供的示例,都觉得蛮合理,但是实际运行起来,有可能并非如此。

写代码实现堆溢出、栈溢出、永久代溢出、直接内存溢出相关推荐

  1. java堆结构,以及堆中的永久代

    转自:java堆的结构以及堆中的永久代 java堆不是数据结构意义上的堆(一种有序的树),而是jvm的堆,也即是运行时的数据区.所有类的实例和数组都是在堆上分配内存,它在JVM启动时被创建,对象所占的 ...

  2. 什么是内存溢出?在哪些区域会发生内存溢出?

    什么是内存溢出?在哪些区域会发生内存溢出?回答这个问题,我们需要先看一看Java代码是怎么运行的.现在计入说我们写了一个 HelloWorld.class: 1 2 3 4 5 6 public cl ...

  3. 五种内存溢出案例总结:涵盖栈深度溢出、永久代内存溢出、本地方法栈溢出、JVM栈内存溢出和堆溢出

    大家好,我是冰河~~ 相信小伙伴们在平时工作的过程中,或多或少都会遇到一个场景:内存溢出.如果你没有遇到过这个场景,那就说明你是个假的程序员.哈哈,开个玩笑,平时工作过程中,我们确实会遇到这个问题.今 ...

  4. java 堆中的永久代_JVM中的堆的新生代、老年代、永久代详解

    JVM中的堆一般分为三大部分:新生代.老年代.永久代,其大致的占比如下: 一.新生代 新生代主要用来存放新生的对象.一般占据堆空间的1/3.在新生代中,保存着大量的刚刚创建的对象,但是大部分的对象都是 ...

  5. 马士兵—JVM—内存溢出—1.线上OOM(内存溢出)问题排查——亲测

    1. 模拟线上oom问题 1.1 代码 @GetMapping("/addList")public void addList(){List list = new ArrayList ...

  6. java内存溢出怎么排查_java线上内存溢出问题排查步骤

    一般线上遇到比较头疼的就是OOM内存溢出问题,我们都会先看错误日志,如果错误日志能够定位出哪个类对象导致内存溢出,那么我们只需要针对问题修改bug就好.但是很多时候我们单凭日志无法定位出内存溢出问题, ...

  7. 内存泄露和溢出的区别_Java 中的内存溢出和内存泄露是什么?我给你举个有味道的例子?...

    JAVA中的内存溢出和内存泄露分别是什么,有什么联系和区别,让我们来看一看. 内存泄漏 & 内存溢出 1. 内存泄漏(memory leak ) 申请了内存用完了不释放,比如一共有 1024M ...

  8. ajax timeout 内存溢出,setInterval 和 setTimeout会产生内存溢出

    setInterval 和 setTimeout会产生内存溢出 来一个简单的例子.有兴趣的朋友可以自己尝试 复制代码 代码如下: function a(){ document.title = &quo ...

  9. java模拟内存溢出并分析_本地模拟内存溢出并分析Dump文件

    java Dump文件分析 前言 dump文件是java虚拟机内存在某一时间点的快照文件,一般是.hprof文件,下面自己模拟一下本地内存溢出,生成dump文件,然后通过mat工具分析的过程. 配置虚 ...

最新文章

  1. Spring配置文件中注入复杂类型属性
  2. [转]Linux 的多线程编程的高效开发经验
  3. Science公布2021年度十大科学突破,AI这项前所未有的突破上榜
  4. 可怕!公司部署了一个东西,悄悄盯着你
  5. 解决linux 系统中Mysql 进程占用CPU 300%故障
  6. 皮一皮:谈恋爱一定要谨慎...
  7. ES-PHP向ES批量添加文档报No alive nodes found in your cluster
  8. 大牛的距离(笑cry)精简算法
  9. 常见排序算法详解 (收藏!)
  10. Attachment rename issue in Faas
  11. 124angular1实现无限表单(仅供自己看)
  12. fpga map测试_一种基于SELECTMAP的可配置且高速的FPGA配置电路及实现方法与流程
  13. RocketMQ初步应用架构理论
  14. 字节跳动高工面试:mysql索引视频教程
  15. Codeforces Round #529 (Div. 3) 题解
  16. Open3d之裁剪点云
  17. 使用REST framework
  18. Linux磁盘管理之df命令详解和使用实例(查看磁盘空间占用情况)
  19. 阿里巴巴:如何从优秀员工,晋升为合格管理者?
  20. Axure rp8元件库载入失败怎么解决

热门文章

  1. 代码何须全部自己写,融云守护程序猿发际线
  2. 诗词-已然绿盈盈蓝点缀
  3. 办公室网络还能这么搭建?从入门到精通,看这里
  4. 面试- 阿里-. 大数据题目- 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?...
  5. 初一数学绩差,需要补习初一数学吗?
  6. 银河麒麟V10(Kylin Linux V10)之DBeaver安装
  7. DBeaver(其他可视化工具一样的逻辑)连接IoTDBDriver教程
  8. chrome单击打开标贴在当前窗口的新标签页设置
  9. 在vue中,用路由router跳转打开一个新标签页
  10. python发行版是什么意思_致 Python 初学者