OutOfMemoryException 翻译(OutOfMemoryError)

OutOfMemoryException 翻译(OutOfMemoryError)插图

Java堆用于存储对象实例,只要不断创建对象,保证GC Roots到对象间有可达路径避免垃圾回收机制清除这些对象

package cn.pengld;

import java.util.ArrayList;

import java.util.List;

/**

* VM Args: -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError

*/

public class HeapSpaceOOMTest {

static class OOMObject{}

public static void main(String[] args) {

List list = new ArrayList<>();

while (true){

list.add(new OOMObject());

}

}

}

由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,所以栈容量只由-Xss参数设定。在单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时,虚拟机抛出的都是 StackOverflowError 异常。

当不限于单线程时,通过不断创建线程的方式可以产生内存溢出异常。

package cn.pengld;

/**

* VM Args: -Xss2M

*/

public class StackSpaceOOMTest {

private void keepGoing(){

while (true){

}

}

public void stackLeakByThread(){

while (true){

Thread thread = new Thread(new Runnable() {

@Override

public void run() {

keepGoing();

}

});

thread.start();

}

}

public static void main(String[] args) {

StackSpaceOOMTest stackSpaceOOMTest = new StackSpaceOOMTest();

stackSpaceOOMTest.stackLeakByThread();

}

}

String类是被final修饰的不可变类,更不能被继承,这样做的目的是为了性能,程序大部分的操作是针对字符串的,将字符串放在常量池中,能有效提高程序运行的效率。若大量的创建字符串对象,会导致常量池内存不够,导致常量池OOM,这也是在处理字符串拼接时使用 StringBuilder对象替代的原因。

package cn.pengld;

import java.util.ArrayList;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

public class ConstantPoolOOMTest {

/**

* -XX:PermSize=10M -XX:MaxPermSize=10M JDK版本号小于 1.7

* Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

*/

public static void jdk6(){

// 保持GC Roots的引用链,避免GC被回收

List list = new ArrayList<>();

int i = 0;

while (true){

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

}

}

/**

*

* 自JDK 7起,原本存放在永久代的字符串常量池被移至Java堆之中

* VM Args: -Xmx6M

*/

public static void jdk7up(){

// 保持GC Roots的引用链,避免GC被回收

Set list = new HashSet<>();

short i = 0;

while (true){

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

}

}

public static void main(String[] args) {

// use 1.6

// jdk6();

// use 7+

jdk7up();

}

}

特别说明:笔者还原了此种情况,运行很多次OOM都发生在HashMap的resize()方法时,但具体的OOM异常取决于具体哪里的对象分配时发生了溢出。参照 《深入理解Java虚拟机:JVM高级特性与最佳实践》(周志明)第三版 2.4.3章节

方法区的主要职责是用于存放类型的相关信息,如类 名、访问修饰符、常量池、字段描述、方法描述等。基本的思路是运行时产 生大量的类去填满方法区,直到溢出为止。不过在JDK 8以后,永久代便完全退出了历史舞台,元空间作为其替代者登场。

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0

下面演示在JDK1.6时如何制造方法区OOM。

cglib

cglib

3.3.0

package cn.pengld;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**

* 借助CGLib使得方法区出现内存溢出异常 jdk 1.6

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

*/

public class MethodAreaOOMTest {

public static void main(String[] args) {

while (true) {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(OOMObject.class);

enhancer.setUseCache(false);

enhancer.setCallback(new MethodInterceptor() {

@Override

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

return proxy.invokeSuper(obj, args);

}

});

enhancer.create();

}

}

static class OOMObject {

}

}

参照《深入理解Java虚拟机-JVM高级特性与最佳实践》(周志明)第二版 2.4.4

package cn.pengld;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**

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

*/

public class DirectMemoryOOMTest {

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 org.fenixsoft.oom.DMOOM.main(DMOOM.java:20)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如有侵权请联系网站管理员删除,联系邮箱1856753@qq.com。

0
显示验证码
没有账号?注册  忘记密码?