专栏首页earthchen的专栏如何构造jvm的堆溢出和栈溢出

如何构造jvm的堆溢出和栈溢出

构造堆溢出和栈溢出

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

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常;—-栈溢出
  • 如果在虚拟机中无法申请到足够多的内存空间,将抛出OutOfMemoryError异常。—-堆溢出

堆溢出

在java堆中只会产生OutOfMemoryError异常

首先,我们知道Java堆内存存放的是对象实例。所以原理上只要我们不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清楚这些对象,也就是说当Eden区满的时候,GC被触发时,让GC误以为内存中的对象还存活着,那么在对象数量达到最大堆容量限制的时候就会产生内存溢出的异常。

public class 堆溢出 {
 
     static class OOMError{}
 
     public static void main(String[] args) {
          List<OOMError> list = new ArrayList<OOMError>();
          while (true) {
               list.add(new OOMError());
          }
     }
}

虽然这里产生了堆溢出,但是我们需要注意产生这个异常的原因是内存溢出还是内存泄露

首先我们要分清楚产生OutOfMemoryError异常的原因是内存泄露还是内存溢出,如果内存中的对象确实都必须存活着而不像上面那样不断地创建对象实例却不使用该对象,则是内存溢出,而像上面代码中的情况则是内存泄露。

如果是内存泄露,我们可以通过一些内存查看工具来查看泄露对象到GC Roots的引用链,找到泄露对象是通过怎样的路径与GC Roots相关联并导致GC无法自动回收这些泄露对象,掌握了这些信息,我们就能比较准确地定位出泄露代码的位置。

如果不是内存泄露,也就是说内存中的对象确实都还必须存活,那么应该检查虚拟机的堆参数,看看是否还可以将机器物理内存调大,同时在代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况。

栈溢出

虚拟机栈用于存储局部变量表、操作数栈、常量池引用等信息。

所以想让栈溢出,我们只需要定义大量的局部变量,增大此方法帧中本地变量表的长度或者设置-Xss参数减少栈内存容量,又或者无限递归调用方法产生新的栈帧都会产生StackOverflowError异常

public class 栈溢出 {
 
     private int stackLength = 1;
 
     public void addStackLength(){
          stackLength++;
          addStackLength();
     }
 
     public static void main(String[] args) throws Throwable{
          栈溢出 oom = new 栈溢出();
          try {
               oom.addStackLength();
          } catch (Throwable e) {
               System.out.println("stack length:" + oom.stackLength);
               throw e;
          }
     }
 
} 

·如果在单线程的情况下,无论是栈帧太大还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机抛出的是StackOverflowError异常。

·在多线程下,不断地建立线程可能会产生OutOfMemoryError异常

方法区中的内存溢出

方法区用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

根据以上存放的数据,让其内存溢出只需要大量添加其中的数据

比如比较容易实现的向运行时常量池中的字符串常量池添加字符串常量

我们可以通过String.intern()方法来构建一个运行时常量池的OutOfMemoryError异常。

String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含了一个等于该String对象的字符串,则返回这个String对象,否则,将此String对象包含的字符串添加到常量池中,并返回这个字符串的String对象的引用。如下面代码:

public class 方法区溢出 {
 
     public static void main(String[] args) {
          List<String> list = new ArrayList<String>();
          int i = 0;
          while (true) {
               list.add(String.valueOf(i++).intern());
          }
     }
 
}

当然,还可以添加大量的类,比如一些框架大量使用反射,如果不具备卸载类的方法,将很快占满方法区

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 服务器TIME_WAIT和CLOSE_WAIT

    TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就会发起主动关闭连接,从而进入TIME_WA...

    用户1637228
  • lru算法和redis的lru

    LRU-K中的K代表最近使用的次数,因此LRU可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”...

    用户1637228
  • java虚拟机

    每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机...

    用户1637228
  • 关于JVM内存溢出的原因分析及解决方案探讨

    当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出。

    技术zhai
  • 从内存泄露、内存溢出和堆外内存,JVM优化参数配置参数

    内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费。

    大数据学习与分享
  • 如何避免内存溢出和频繁的垃圾回收

    垃圾回收完成后,一般是需要进行内存碎片管理,将不连续的空闲内存移动到一起,以便空出足够的连续内存空间供后续使用。

    王小明_HIT
  • 经典面试题-Java中,String和StringBuffer的区别?

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    cwl_java
  • Java内存故障?只是因为你不够帅!

    从小我就对Java有着深厚的感情,算下来有几十年的Java经验了。当年的Java还是Sun公司的,我有着多年的Servlet经验,CURD经验,在现在已经被自我...

    xjjdog
  • iOS - 老生常谈内存管理(二):从 MRC 说起

    MRC全称Manual Reference Counting,也称为MRR(manual retain-release),手动引用计数内存管理,即开发者需要手动...

    师大小海腾
  • 【深入理解Java原理】垃圾回收原理

    初始标记会触发 stop the world ,从垃圾回收的根对象开始查找,这个过程会暂停整个JVM,但是很快结束

    王小明_HIT

扫码关注云+社区

领取腾讯云代金券