前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手写jvm中的各种OOM

手写jvm中的各种OOM

作者头像
温安适
发布2018-05-17 16:36:00
1.5K0
发布2018-05-17 16:36:00
举报
文章被收录于专栏:温安适的blog温安适的blog

 前言

    大家好,这篇blog不写什么实际技术,就把我从书上学来的,制造JVM各种OOM的方法告诉大家。下回在遇到有人问你Java会内存溢出吗?你可以快速回答他,会!我还会写各种bug,造成JVM出现OOM异常。

知己知彼,JVM的各个区域的特定

要想写出各种OOM,必须知道JVM各个区域的特点,以便针对性的写bug,造成OOM。下面是我看书后总结的JVM各个区域的特点:

区域名称

作用

是否线程私有

是否会 内存溢出

溢出原因

程序计数器

当前线程所执行的字节码的行号的指示器。 每个线程都有独立的程序计数器

Java虚拟机栈

与线程同生命周期存储局部变量表,操作数栈 动态链接,方法出口,对象引用等。 局部变量表存储基本数据类型 boolean,int,float,long, double,byte,short. long,double占2其余占1局部空间

2种异常 1.StackOverflowError, 线程请求的栈深度大于虚拟机所允许的深度。 2.OutOfMemoryError 栈扩展时申请到不足够的内存。

本地方法栈

Java虚拟机栈类似 为Native服务

2种异常 1.StackOverflowError,线程请求的栈深度大于 虚拟机所允许的深度。 2.OutOfMemoryError 栈扩展时申请到不足够的内存。

java堆

存放对象实例以及数组。 GC堆。 逻辑连续,物理不连续 通过-Xmx和-Xms来控制。

堆中内存不足,无法完成实例分配,并且堆无法再扩展时。 将抛出OutOfMemoryError

方法区

non-heap,“永久代” 受到-XX:MaxPermSize

当方法区无法满足内存分配需求时OutOfMemoryError

运行时常量池

字面量,符号引用。Java语言 不一定要求只有编译才会产生常量 String的interm()是方法区的一部分 JDK1.7将它从方法区移除,使用直接内存

受方法区限制(1.7以后不会).当常量池无法再申请到内存 时会内存溢出

直接内存

即本机直接内存,不受JVM控制。

JVM各内存区域总和大于物理内存时, 当再动态扩展时会OutOfMemoryError

 逐个击破,实战各种OOM

下面的例子均来自《深入理解Java虚拟机第二版本》,不过我结合了IDEA进行了实战操作,对设置参数进行了注释,也算是精炼了实战OOM的方法。

java堆(GC堆)的OOM

java堆出现OOM的情况如下:

 堆中没有内存完成实例分配时,并且堆无法再扩展时。将抛出OutOfMemoryError。

为了让java堆(GC堆)更容易出现OOM,我们需要把JVM的堆内存分配的小一点,需要用到的参数如下:

  -Xms20m (JVM初始分配的堆内存)

  -Xmx20m(最大可使用内存)

  -XX:+HeapDumpOnOutOfMemoryError(r,JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”)(可以不设置,对造成OOM没有帮助)

在IDEA中设置这些参数的方法如下:

1.先运行一遍写好的程序

2.之后在Run选项中选择Edit Configurations

之后如下图进行设置。

造成GC堆OOM的代码如下:

    其核心就是不断的生产对象,并保证已生产对象存活。利用List,维护所有OOMObject对象存活(利用list保存所有OOMObject都有引用),并利用集合自动扩展申请新的内存,直至Java堆剩余空间,不满足新的OOMObject对象所需的空间为止。

代码语言:javascript
复制
/**
 * Created by Administrator on 2017/6/22.
 * -Xms20m (JVM初始分配的堆内存)-Xmx20m(最大可使用内存)
 * -XX:+HeapDumpOnOutOfMemoryError(r,JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”)
 */
public class HeapOOM {

    static class OOMObject{

    }

    public static void main(String[]args){
       //利用List,维护所有OOMObject对象存活,并利用集合自动扩展申请新的内存。
        List<OOMObject>list=new ArrayList<OOMObject>();
        while(true){
            list.add(new OOMObject());
        }
    }
}

Java虚拟机栈OOM

虚拟机栈理论上有2种异常:   1.StackOverflowError,线程请求的栈深度大于虚拟机所允许的深度。   2.OutOfMemoryError栈扩展时申请到不足够的内存。

实验中,StackOverflowError非常容易出现,OutOfMemoryError从未出现过,网友可以尝试下。

下面仅介绍如何生成StackOverflowError。

为了让JVM,更容易出现StackOverflowError,我们需要设置如下参数:

-Xss128k(设置每个线程的堆栈大小 为128K)。设置方法如上,不在赘述。

造成虚拟机栈StackOverflowError的方法

这里利用死递归(没有出口的递归),不断的往虚拟机栈中加入递归上下文信息。

代码语言:javascript
复制
/**
 * Created by Administrator on 2017/6/22.
 * -Xss128k(设置每个线程的堆栈大小 为128K)
 */
public class JavaVMStackSOF {

    private int stackLength=1;

    public void stackLeak(){
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {
        JavaVMStackSOF sof=new JavaVMStackSOF();
        sof.stackLeak();
    }
}

方法区内存溢出

方法区存放类的相关信息,我们可以不断生成新的类信息到方法区,直到撑爆方法区。

如何动态产生类信息呢?JavaAPI中有反射, 但是很多框架中都是用CGLib,例如Spring。这里也使用CGLib.

其代码如下:

代码语言:javascript
复制
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * Created by Administrator on 2017/6/23.
 */
public class JavaMethodAreaOOM {

    static  class OOMObject{

    }

    public static void main(String[] args) {
        while (true){
            //创建增强器
            Enhancer enhancer=new Enhancer();
            //设置要增强的父类
            enhancer.setSuperclass(OOMObject.class);
            //不使用缓存很重要,保证更容易触发OOM
            enhancer.setUseCache(false);
            //不增强任何方法
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invoke(o,objects);
                }
            });
           //创建增强后的类
            enhancer.create();
        }
    }
}

本地内存直接溢出

Java中何时会使用动态内存呢?NIO中的Channel,使用NIO比较繁琐,我们使用直接分配本地内存的方式,造成内存溢出。Unsafe类可以直接分配本地内存。

为了让容易造成本地内存直接溢出,我们设置参数:

--XX:MaxDirectMemorySize=10M(JVM使用本地内存上限为10M)

设置方式如上。

造成本地内存直接溢出的代码为:

代码语言:javascript
复制
import sun.misc.Unsafe;
import java.lang.reflect.Field;

/**
-Xmx20m --XX:MaxDirectMemorySize=10M
 * Created by Administrator on 2017/6/23.
 */

public class DirectMemoryOOM {
    private  static final  int _1MB=1024*1024;

    public static void main(String[] args) throws IllegalAccessException {
        //theUnsafe;d对象
        Field unsafeField= Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        //但是如果字段是静态字段的话,传入任何对象都是可以的,包括null,这里获得theUnsafe对象
        Unsafe unsafe= (Unsafe) unsafeField.get(null);
        while (true){
            //指派内存1MB
            unsafe.allocateMemory(_1MB);
        }
    }
}

总结

到此我们对JVM大部分区域,进行了针对性的OOM实战。有一些没有实战的,网友可以自行尝试。

查阅了这篇文章,我希望你可以很自豪的说,我能够写bug,造成JVM内存溢出了!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  •  前言
  • 知己知彼,JVM的各个区域的特定
  •  逐个击破,实战各种OOM
    • java堆(GC堆)的OOM
      • 在IDEA中设置这些参数的方法如下:
      • 造成GC堆OOM的代码如下:
    • Java虚拟机栈OOM
      • 造成虚拟机栈StackOverflowError的方法
    • 方法区内存溢出
      • 本地内存直接溢出
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档