`
liulanghan110
  • 浏览: 1064303 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

JAVA内存错误实战

    博客分类:
  • JAVA
阅读更多

1.JAVA堆导致的内存异常

Java堆存放的是对象实例,因此只要不断建立对象,并且保证GC Roots到对象之间有可达路径即可产生OOM异常。这是由于内存泄露导致的内存溢出异常。代码如下:

package MemoryTest;

import java.util.ArrayList;
import java.util.List;

/**
 * Java堆OOM测试
 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

 * @author zzm

 */
public class HeapOOM {

       static class OOMObject {

       }

       public static void main(String[] args) {

              List<OOMObject> list = new ArrayList<OOMObject>();
              while (true) {

                     list.add(new OOMObject());
              }
       }
}

  

 测试中限制Java堆大小为20M,不可扩展,通过参数-XX:+HeapDumpOnOutOfMemoryError让虚拟机在出现OOM异常的时候Dump出内存映像以便分析。

注:虚拟机参数设置。窗口->首选项->JAVA->已安装的JRE,在缺省的VM自变量中增加。

 

运行结果:

stack length:11347
Exception in thread "main" java.lang.StackOverflowError
	at MemoryTest.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:16)

 

 

 

 关于VM栈和本地方法栈在VM Spec描述了两种异常:StackOverflowErrorOutOfMemoryError,当栈空间无法继续分配分配时,到底是内存太小还是栈太大其实某种意义上是对同一件事情的两种描述而已.

 

1.使用-Xss参数削减栈内存容量。结果:抛出SOF异常时的堆栈深度相应缩小。

2.定义大量的本地变量,增大此方法对应帧的长度。结果:抛出SOF异常时的堆栈深度相应缩小。

3.创建几个定义很多本地变量的复杂对象,打开逃逸分析和标量替换选项,使得JIT编译器允许对象拆分后在栈中分配。结果:实际效果同第二点。

 

 

 

 

 

stack length:2402
Exception in thread "main" java.lang.StackOverflowError
	at MemoryTest.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
	at MemoryTest.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:16)

 

如果在多线程环境下,不断建立线程倒是可以产生OOM异常,但是基本上这个异常和VM栈空间够不够关系没有直接关系,甚至是给每个线程的VM栈分配的内存越多反而越容易产生这个OOM异常。

 

原因其实很好理解,操作系统分配给每个进程的内存是有限制的,譬如32Windows限制为2GJava堆和方法区的大小JVM有参数可以限制最大值,那剩余的内存为2G(操作系统限制)-Xmx(最大堆)-MaxPermSize(最大方法区),程序计数器消耗内存很小,可以忽略掉,那虚拟机进程本身耗费的内存不计算的话,剩下的内存就供每一个线程的VM栈和本地方法栈瓜分了,那自然每个线程中VM栈分配内存越多,就越容易把剩下的内存耗尽。

 

创建线程导致OOM异常

package MemoryTest;

/*
 * 创建线程导致OOM异常

 * VM Args:-Xss2M (这时候不妨设大些)

 * @author zzm

 */

public class JavaVMStackOOM {

 

       private void dontStop() {

              while (true) {

              }

       }

       public void stackLeakByThread() {

              while (true) {

                     Thread thread = new Thread(new Runnable() {

                            @Override

                            public void run() {

                                   dontStop();

                            }

                     });

                     thread.start();

              }

       }

       public static void main(String[] args) throws Throwable {

              JavaVMStackOOM oom = new JavaVMStackOOM();

              oom.stackLeakByThread();
       }
}

 

 设置好虚拟机参数后,运行上面的代码。特别提示运行上面这段代码,记得要存盘当前工作,上述代码执行时有很大令操作系统卡死的风险。结果:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread 


 

 

 

运行时常量池

 

 

要在常量池里添加内容,最简单的就是使用String.intern()这个Native方法。由于常量池分配在方法区内,我们只需要通过-XX:PermSize-XX:MaxPermSize限制方法区大小即可限制常量池容量。实现代码如下:

 

package MemoryTest;

import java.util.ArrayList;
import java.util.List;

/*
 
 * 运行时常量池导致的OOM异常
 
 * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M

 * @author zzm

 */
public class RuntimeConstantPoolOOM {

    public static void main(String[] args) {

              // 使用List保持着常量池引用,压制Full GC回收常量池行为

              List<String> list = new ArrayList<String>();

              // 10M的PermSize在integer范围内足够产生OOM了

              int i = 0; 

              while (true) {

                     list.add(String.valueOf(i++).intern());

              }

       }

}

 

设置好虚拟机参数后运行,结果如下:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
	at java.lang.String.intern(Native Method)
	at MemoryTest.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:29)

 

方法区

方法区用于存放Class相关信息,所以这个区域的测试我们借助CGLib直接操作字节码动态生成大量的Class,值得注意的是,这里我们这个例子中模拟的场景其实经常会在实际应用中出现:当前很多主流框架,如SpringHibernate对类进行增强时,都会使用到CGLib这类字节码技术,当增强的类越多,就需要越大的方法区用于保证动态生成的Class可以加载入内存。

 

package MemoryTest;
import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;

/* 需要下载cglib
 
 * 借助CGLib使得方法区出现OOM异常

 * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M

 * @author zzm

 */

public class JavaMethodAreaOOM {

 

       public static void main(String[] args) {

              while (true) {

                     Enhancer enhancer = new Enhancer();

                     enhancer.setSuperclass(OOMObject.class);

                     enhancer.setUseCache(false);

                     enhancer.setCallback(new MethodInterceptor(){

                            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

                                   return proxy.invokeSuper(obj, args);

                            }

                     });

                     enhancer.create();

              }

       }

 

       static class OOMObject {

       }

}

 结果:

Caused by: java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)

 

本机直接内存

package MemoryTest;


import java.lang.reflect.Field;
import sun.misc.Unsafe;

/*
 
 * 本机直接内存

 * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M

 * @author zzm

 */

public class DirectMemoryOOM {

 

       private static final int _1MB = 1024 * 1024;

 

       public static void main(String[] args) throws Exception {

              Field unsafeField = Unsafe.class.getDeclaredFields()[0];

              unsafeField.setAccessible(true);

              Unsafe unsafe = (Unsafe) unsafeField.get(null);

              while (true) {

                     unsafe.allocateMemory(_1MB);

              }

       }

}

 设置好参数后运行,结果

 

Exception in thread "main" java.lang.OutOfMemoryError
	at sun.misc.Unsafe.allocateMemory(Native Method)
	at MemoryTest.DirectMemoryOOM.main(DirectMemoryOOM.java:35)

 

 

 

 2.VM栈和本地方法栈

 设置虚拟机参数后运行程序,结果如下:

分享到:
评论

相关推荐

    实战Java高并发程序设计(第2版)PPT模板.pptx

    1 2.1有关线程你必须知道的事 3 2.3volatile与Java内存模型(JMM) 2 2.2初始线程:线程的基本操作 4 2.4分门别类的管理:线程组 2java并行程序基础 5 2.5驻守后台:守护线程(Daemon) 6 2.6先做重要的事:线程...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    书名:《Java开发实战1200例(第I卷)》(清华大学出版社.李钟尉,陈丹丹) PDF格式扫描版,全书分为24章,共817页。2011年1月出版。 全书压缩打包成4部分,这是第3部分 注:本系列图书的第I、II卷再版时均相应改名为...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    第2章 Java内存区域与内存溢出异常 / 24 2.1 概述 / 24 2.2 运行时数据区域 / 25 2.2.1 程序计数器 / 25 2.2.2 Java虚拟机栈 / 26 2.2.3 本地方法栈 / 27 2.2.4 Java堆 / 27 2.2.5 方法区 / 28 2.2.6 运行...

    Java虚拟机

    原子性、可见性和有序性在Java内存模型中的体现;先行发生原则的规则和使用;线程在Java语言中的实现原理;虚拟机实现高效并发所做的一系列锁优化措施。 前言 第一部分 走近Java 第1章 走近Java 1.1 概述 1.2 ...

    JVM入门到JVM 调优实战

    第三节:Java内存模型 ························································ 35 第四节:内存屏障与JVM指令 ··················...

    Hadoop实战(第2版)

    技术点41 内存交换技术点42 磁盘健康技术点43 网络6.3 可视化技术点44 提取并可视化任务执行时间6.4 优化 .6.4.1 剖析MapReduce 的用户代码 技术点45 剖析map 和reduce 任务 6.4.2 参数配置6.4.3...

    Web服务器三剑客运维配置实战 Nginx+JVM+Tomcat+HTTP协议.zip

    ├─3.03 Nginx进阶-配置提升-Nginx错误页面.mp4 ├─3.04 Nginx进阶-配置提升-访问控制.mp4 ├─3.05 Nginx进阶-配置提升-流量控制.mp4 ├─3.06 Nginx进阶-提升配置-流量控制.mp4 ├─3.07 Nginx进阶-配置提升-...

    Java开发技术大全 电子版

    2.7基础语法实战演习88 2.7.1判断闰年88 2.7.2求最大公约数和最小公倍数89 2.7.3Fibonacci数列90 2.7.4逆向输出数字91 2.7.5求水仙花数92 2.7.6输出图形93 2.7.7输出九九口诀表94 2.8本章小结95 第2篇Java...

    JVM是如何分配管理内存的?

    写在前面:博主是一只经过实战开发历练后投身培训事业的“小山猪”,昵称取自动画片《狮子王》中的“彭彭”,总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘...

    springmybatis

    其实还有更简单的方法,而且是更好的方法,使用合理描述参数和SQL语句返回值的接口(比如IUserOperation.class),这样现在就可以至此那个更简单,更安全的代码,没有容易发生的字符串文字和转换的错误.下面是详细...

    Hadoop硬实战 [(美)霍姆斯著][电子工业出版社][2015.01]_PDF电子书下载 带书签目录 高清完整版.rar )

    6.4.5 在MapReduce 中优化用户的Java 代码 6.4.6 数据序列化 6.5 本章小结 第4 部分 数据科学. 7 数据结构和算法的运用 7.1 使用图进行数据建模和解决问题 7.1.1 模拟图 7.1.2 最短路径算法 技术...

    软件框架设计的艺术

    17.3.1 我想修正犯下的错误 316 17.3.2 第二天的解决方案 317 17.4 第三天:评判日 320 17.5 也来玩下这个游戏吧 327 第18章 可扩展Visitor模式的案例 328 18.1 抽象类 331 18.2 为改进做好准备...

    【05-面向对象(下)】

    •在java类里只能包含Field,方法,构造器,初始化块,内部类(接口、枚举)等5种成员。 用static修饰的类成员属 于类成员,类Field既可通过类来访问,也可以通过类的对象来访问。当通过对象来访问类属性时,系统会在底...

    深入浅出Struts 2 .pdf(原书扫描版) part 1

    这是因为某个servlet(服务器端Java程序)在应用户的请求而首次调入内存执行之后将一直驻留在内存里,对同一个servlet的后续请求不用再对这个servlet的类进行实例化,因此响应速度更快。 可是,servlet也存在一个...

Global site tag (gtag.js) - Google Analytics