JVM中常见的OOM,那么如何通过自己编写代码产生这些OOM异常呢?通过写代码重现异常,是为了避免在工作中写出有OOM BUG的代码。之前虽然看过相关文章,但是没自己写过这些代码,这次在编写的实际过程中,由于和书本使用的JDK版本不一致,也会有点问题。其中印象最深刻的就是从JDK1.7开始常量池就已经不放在方法区了,而是改到了Java堆中,所以《深入理解JAVA虚拟机》中的有些知识也需要更新了。下面的代码基于JDK1.7来的。并且在运行程序的时候需要设置JVM参数,如果不设置,轻则需要等待很长时间才会出现异常,重则系统假死甚至导致系统内存溢出。

在测试直接内存的时候,引用了rt.jar中的sun.misc.Unsafe类,如果使用了Eclipse作为IDE,需要修改windows-->preferences-->java-->compiler-->Errors/Warinings,选择Deprecated and restricted API,将Forbidden reference(access rules)修改成ignore。

 package org.zsl.learn.oom;

 import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List; import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import sun.misc.Unsafe; /**
* 测试在代码中如何产生堆内存溢出、栈溢出(超出长度)、栈内存溢出(栈不能扩展的情况下OOM)、方法区内存溢出、常量池内存溢出
* JDK1.7
* @author Administrator
*
*/
public class TestOOM {
private static int count = 1;
private static final int _1MB = 1024*1024; List<String> list = new ArrayList<String>(); //一个普通的对象
static class OOMObjectClass{
public OOMObjectClass(){}
} /**
* 通过list对象保持对对象列表的引用,不然GC收集对象,然后不断地向列表中添加新的对象,就会发生OOM
*
* @VM args:-verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError
*/
public void testHeapOOM(){
List<OOMObjectClass> list = new ArrayList<>();
while(true){
list.add(new OOMObjectClass());
}
} /**
* 通过递归调用方法,从而让方法栈产生栈 StackOverflowError
*
* @VM args:-verbose:gc -Xss128k
*/
public void stackLeak(){
count++;
stackLeak();
} /**
* 除了上述的递归调用可以产生溢出外,还有就是过多的线程,当栈内存无法动弹扩展是,会出现OOM
*
* 由于在Window的JVM中,Jave的线程是映射到了操作系统的内核线程上,故而这段代码的运行时非常危险的
* 笔者运行的时候限制了JVM内存大小,但是栈内存可以动态扩展,所以电脑内存直接到了90%以上,我果断停止了程序的运行
* 由于栈内存只由-Xss参数控制,并没有办法让其不自动扩展,所以这段代码非常危险
* 参数:-verbose:gc -Xms10M -Xmx10M -Xss2M
*/
public void stackLeakByThread(){
while(true){
Thread t = new Thread(new Runnable() { @Override
public void run() {
while (true){ }
}
});
t.start();
count++;
}
} /**
* 常量池是存在于方法区内的,故而只要限制了方法区的大小,当不断新增常量的时候就会发生常量池的溢出
*
* 笔者使用的是JDK1.7 64位,此时的常量池已经不存在与方法区中,而是迁移到了堆中,故而测试的时候需要限制JVM的堆大小,且不能自动扩展
* @VM args: -Xms10M -Xmx10M
*/
public void constantPoolOOM(){
int i=0;
while(true){
list.add(String.valueOf(i++).intern()); //String类型的intern方法是将字符串的值放到常量池中
}
} /**
* 方法区是存放一些类的信息等,所以我们可以使用类加载无限循环加载class,这样就会出现方法区的OOM异常
* 主要,使用内部类的时候,需要要使用静态内部类,如果使用的是非静态内部类,将不会发生方法区OOM
* 使用了CGLib直接操作字节码运行时,生成了大量的动态类
* 需要者两个jar包:cglib-2.2.2.jar asm-3.1.jar
* @VM args:-XX:PermSize=10M -XX:MaxPermSize=10M
*/
public void methodAreaOOM(){
while(true){
Enhancer eh = new Enhancer();
eh.setSuperclass(OOMObjectClass.class);
eh.setUseCache(false);
eh.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
return arg3.invokeSuper(arg0, arg2);
}
});
eh.create();
}
} /**
* 要讨论这部分的内存溢出,首先必须要说一下什么是直接内存:
* 直接内存并不是JVM运行时数据区的一部分,也不是JVM规范中定义的内存区域,但是这部分内存也被频繁的使用,也会产生OOM。
* JDK1.4中新加入了NIO类,引入了一种Channel与Buffer的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在JAVA堆里面的DirectByteBuffer对象作为
* 这些堆外内存的引用进而操作,这样在某些场景中可以显著的提高性能,避免了在native堆和java堆中来回复制数据。这这部分堆外内存就是直接内存了。
*
* 直接内存虽然不会受到JAVA堆大小的限制,但是还是会受到本机内存大小的限制,故而服务器管理员在设置JVM内存管理参数的时候,如果忘记了直接内存,那么当程序进行动态扩展的时候,就有可能发生OOM
* 直接内存的容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,那么默认与JAVA堆得最大值一样。
*
* @VM args:-Xmx20M -XX:MaxDirectMemorySize=10M
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public void directMemoryOOM() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
while(true){
unsafe.allocateMemory(_1MB);
}
} public static void main(String[] args) {
TestOOM oom = new TestOOM();
// ---------测试堆内存溢出-----------
// oom.testHeapOOM(); // ---------测试栈溢出----------
// try{
// oom.stackLeak();
// }catch(Throwable error){
// System.out.println("Stack length-->"+count);
// throw error;
// } // ---------测试由于栈动态扩展导致的OOM----------
// try{
// oom.stackLeakByThread();
// }catch(Throwable error){
// System.out.println("Stack length-->"+count);
// throw error;
// } // ----------测试方法区溢出----------
// oom.methodAreaOOM(); // ----------测试常量池溢出----------
// oom.constantPoolOOM(); // ----------测试直接内存溢出---------- try {
oom.directMemoryOOM();
} catch (Exception e) {
System.out.println(e);
} } }

【3】JVM-OutOfMemory异常重现的更多相关文章

  1. 使用visual studio 2015调用阿里云oss .net sdk 2.2的putobject接口抛出outofmemory异常

    问题描述: 使用阿里云oss .net sdk 2.2版本,使用putobject接口上传文件时,抛出outofmemory异常. 原因分析: 上传时,用于准备上传的数据缓冲区内存分配失败.与应用软件 ...

  2. @几种OutOfMemory异常

    Java虚拟机运行时数据区 在Java虚拟机规范的描述中,除了程序计数器之外,虚拟机内存的其他几个运行时区域都会发生OutOfMemory异常的可能. 我们可以在IDE(如IDEA)中设置虚拟机启动参 ...

  3. 【JVM系列1】深入分析Java虚拟机堆和栈及OutOfMemory异常产生原因

    前言 JVM系列文章如无特殊说明,一些特性均是基于Hot Spot虚拟机和JDK1.8版本讲述. 下面这张图我想对于每个学习Java的人来说再熟悉不过了,这就是整个JDK的关系图: 从上图我们可以看到 ...

  4. JVM内存异常与常用内存参数设置总结

    Java Web程序由于引入大量第三方java类库,在启动时经常会遇到内存溢出(Memory Overflow)或者内存泄漏(Memory leak)问题,导致程序启动失败. 一.OOM异常分类: O ...

  5. JVM的异常体系

    任何程序都追求正确有效的运行,除了保证我们代码尽可能的少出错之外,我们还要考虑如何有效的处理异常,一个良好的异常框架对于系统来说是至关重要的.最近在采集框架的时候系统的了解一边,收获颇多,特此记录相关 ...

  6. ORA-08102异常重现及恢复

    现象: 在表上面新建主键报ORA-08102的异常: SQL> alter table t add primary key(id); alter table t add primary key( ...

  7. JVM OOM异常会导致JVM退出吗?

    出处:  https://mp.weixin.qq.com/s/8j8YTcr2qhVActLGzOqe7Q  https://blog.csdn.net/h2604396739/article/de ...

  8. JVM --- OutOfMemoryError异常

    在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有可能发生OutOfMemoryError(OOM)异常. 1.Java堆溢出 Java堆用于存储对象实例,只要不断地创 ...

  9. 关于Myeclipse下的JVM启动异常的问题:Out Of Memery / Could not reserve enough space for object heap

    以下都是初步理解: 这主要是JVM内存配置的问题,网上有很多参考资料,但是我们不能死板硬套,必须根据自己工程的实际情况配置相关的参数. 1.按照jvm的设计规则,JVM内存分为堆(Heap)和非堆(N ...

随机推荐

  1. Vuex 实践讲解

    state 用来数据共享数据存储 mutation 用来注册改变数据状态 getters 用来对共享数据进行过滤操作 action 解决异步改变共享数据 这个四大特征就是核心,如何用怎么用 接下来还是 ...

  2. su 和 su -

    单纯使用su切揣到root,读取变量的方式 是non-login shell,这种方式下很多的变量都 不会改变,尤其是PATH,所以root用的很多命令都只能用绝对路径来执行,这种方式只是切换到roo ...

  3. android工程导入没有错误,运行提示Unable to instantiate activity ComponentInfo

    导入小米clientside_android_sdk的demo OAuth-OpenAuthDemo,点Java Build Path的Libraries内Add External JARs,将oau ...

  4. 理解FlumeNG的batchSize和transactionCapacity参数和传输事务的原理 【转】

    基于ThriftSource,MemoryChannel,HdfsSink三个组件,对Flume数据传输的事务进行分析,如果使用的是其他组件,Flume事务具体的处理方式将会不同. Flume的事务处 ...

  5. 多媒体文件格式之AVI

    [时间:2016-07] [状态:Open] AVI(Audio Video Interleaved的缩写)是一种RIFF(Resource Interchange File Format的缩写)文件 ...

  6. FIS-PLUS百度前端框架使用过程

    1.如果后端开发语言是php,那么前端fis框架用FIS-PLUS,如果是java则用jello 2.FIS-PLUS使用步骤 1.安装nodejs 2.安装 fis npm install -g f ...

  7. JIRA python篇之统计产品尚未解决的bugs

    [本文出自天外归云的博客园] 通过python中的jira类我们可以方便的操作jira,获取一些我们想要再加工的信息. 一些通过JIRA的JTL查询语句不方便直接搜索的过滤条件可以通过JIRA的pyt ...

  8. 这到底是什么bug?---已结贴

    问题描述:全局变量,会被莫名其妙更改!打印为50,后面做比较的时候这个值为0了. 第一,我肯定没有犯低级错误,没有其他的更改,搜索全部代码,没有发现这个变量因为我程序问题导致不符合预期,同时找了两位同 ...

  9. 再论FreeRTOS中的configTOTAL_HEAP_SIZE

    关于任务栈和系统栈的基础知识,可以参考之前的随笔.(点击这里) 这里再次说明:#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) 这个 ...

  10. [转]oracle存储过程中update不成功的一个原因

    原文地址:http://lin49940.iteye.com/blog/466626 今天一个同事写oracle 的存储过程遇到了一个问题, 他在里面update 操作不能完成更新的操作, 但是又不会 ...