文档章节

手写jvm中的各种OOM

温安适
 温安适
发布于 2017/06/28 22:22
字数 1706
阅读 237
收藏 5

 前言

    大家好,这篇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对象所需的空间为止。

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

 

 

© 著作权归作者所有

共有 人打赏支持
上一篇: 剖析1条JMS消息
下一篇: 浅析AQS
温安适
粉丝 102
博文 22
码字总数 37443
作品 0
朝阳
后端工程师
私信 提问
jvm堆内存溢出后,其他线程是否可继续工作

最近网上出现一个美团面试题:“一个线程OOM后,其他线程还能运行吗?”。我看网上出现了很多不靠谱的答案。这道题其实很有难度,涉及的知识点有jvm内存分配、作用域、gc等,不是简单的是与否...

别打我会飞
10/31
0
0
JVM内存管理:深入Java内存区域与OOM

Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。 概述: 对于从事C、C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高...

underA
2013/10/24
0
0
深入理解Java虚拟机:JVM高级特性与最佳实践(一):java 内存区域与内存异常

如需转载,请标明转自何处 运行时数据区域: java 虚拟机在执行java程序的过程中会把他管理的内存化为若干个不同的数据区域。这些区域都有各自的用途,销毁与创建的时间,有的区域随着进程的...

熊大熊二
2015/11/03
0
0
Android Out Of Memory(OOM) 的详细研究

基于Android开发多媒体和游戏应用时,可能会挺经常出现Out Of Memory 异常 ,顾名思义这个异常是说你的内存不够用或者耗尽了。 在Android中,一个Process 只能使用16M内存,如果超过了这个限...

caikezhan
2013/10/15
0
0
BATJ等大厂最全经典面试题分享

金九银十,又到了面试求职高峰期,最近有很多网友都在求大厂面试题。正好我之前电脑里面有这方面的整理,于是就发上来分享给大家。 这些题目是网友去百度、蚂蚁金服、小米、乐视、美团、58、...

老道士
09/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Git —— 创建版本库和提交回退版本

二、 创建版本库 版本库又叫做仓库,简单理解就是一个目录,这个目录里面所有的文件都可以被Git管理起来,每个文件的修改、删除,Git都可以跟踪,便于追踪历史与还原。找到一个合适的位置,创...

lwenhao
28分钟前
3
0
guava cache使用介绍

今天在项目中发现大量使用guava cache提供缓存,觉得不错。 jvm堆大小为5G /** * * 占用JVM内存,内部数据结构类似于ConcurrentHashMap。因为JVM堆大小的限制,guava cac...

jack_peng
33分钟前
3
0
崛起于Springboot2.X之投票活动排行榜项目

简介:投票活动,用户只能一天投票一次,然后对参与投票的项目进行实时的排行功能。 架构:redis+mysql+springboot2.0.3+mybatis 不懂可以私信我哦 1、数据库建表 CREATE TABLE `t_dtb_prod...

木九天
41分钟前
2
0
logback源码分析-2、appender解析

源码基于logback 1.1.2 logback.xml文件内容如下 <?xml version="1.0"?><configuration scan="true" scanPeriod="30 seconds"> <property name="fileDir" value="/export/log/ingore......

924411018
48分钟前
2
0
【HAVENT原创】NodeJS 两个模块进行 RSA 加密解密(匹配Java RSA)

业务逻辑需要使用 NodeJS 进行公钥加密传输给 Java 后端进行私钥解密,但是默认 NodeJS 使用的 RSA padding 模式与 Java 的不一致,所以需要配置。 不啰嗦,上代码,分别用 crypto 和 node-r...

HAVENT
54分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部