手写jvm中的各种OOM

 前言

    大家好,这篇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

212332_MJz9_2296755.png

之后如下图进行设置。

212647_3n9v_2296755.png

造成GC堆OOM的代码如下:

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

/**
 * 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的方法

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

/**
 * 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.

其代码如下:

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)

设置方式如上。

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

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内存溢出了!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值