JVM OutOfMemoryError

Error的层级结构:

java.lang
类 OutOfMemoryError

java.lang.Object
  java.lang.Throwable
      java.lang.Error
          java.lang.VirtualMachineError
              java.lang.OutOfMemoryError
所有已实现的接口: 
Serializable 

 

API中的说明:因为内存溢出或没有可用的内存提供给垃圾回收器时,Java虚拟机无法分配一个对象,这时抛出该Error

需要理解JVM的内存划分,堆(heep)和栈(stack)。

堆中存放Object对象数据,例如new出来的Object。当没有任何引用指向某对象时,该对象可能被垃圾回收。有关垃圾回收算法,可参考其他有关文章,网上很多。关于对象引用,按强弱还有强引用,软引用,弱引用,虚引用之分。强引用,一般不会被垃圾回收,若内存不足,只好抛出OutOfMemoryError;软引用,垃圾回收器扫描时,若是内存不足的情况下,这种引用的对象会被回收。弱引用,只要垃圾回收器扫描,就会回收。

栈是动态的,每起一个线程就会起一个栈,每个线程中每个方法会在栈中起一个帧,称栈帧;每一帧中存的是方法的局部变量,中间值等信息。方法执行结束,栈帧内存释放,线程结束栈内存释放。

下面的内容引用自51CTOevan2012的一篇文章:JVM的内存溢出异常

在Java虚拟机规范的描述中,除了PC(程序计数器)寄存器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError的可能。当发生OutOfMemoryError时,无法用try...catch捕捉。

简单介绍几个JVM的参数设置:

-Xss128k

每个线程的java栈大小,一个线程java栈所有栈帧大小总和不能超过128k。

-Xms128m

表示JVM Heap(堆内存),初始分配128MB

-Xmx512m

表示JVM Heap(堆内存)最大允许的尺寸512MB。

-XX:PermSize=20M

设置方法区的初始大小

-XX:MaxPermSize=30M

设置方法区的最大值

关于这些参数的设置,详细可以查看JVM性能调优方面的文章。

 

Java栈内存溢出

在Java虚拟机规范中,对这个区域规定了两种异常状况:StackOverflowError和OutOfMemoryError异常。

1.StackOverflowError异常

每当java程序代码启动一个新线程时,Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。当线程调用java方法时,虚拟机压入一个新的栈帧到该线程的java栈中。只要这个方法还没有返回,它就一直存在。如果线程的方法嵌套调用层次太多(如递归调用),随着java栈中帧的逐渐增多,最终会由于该线程java栈中所有栈帧大小总和大于-Xss设置的值,而产生StackOverflowError内存溢出异常。例子如下:

package leon.java.outofmemory;

public class StackOverflow {
	private static int count = 0;

	public static void main(String[] args) {
		method();
	}

	public static void method() {
		System.out.println(++count);
		method();
	}

}

该代码执行,到count到某个值时,抛出java.lang.StackOverflowError

随着-Xss参数值的增大,可以嵌套的方法调用层次也相应增加。

综上所述,StackOverflowError异常是由于方法调用的层次太深,最终导致为某个线程分配的所有栈帧大小总和大于-Xss设置的值,从而发生StackOverflowError异常。

2.OutOfMemoryError异常

java程序代码启动一个新线程时,没有足够的内存空间为该线程分配java栈(一个线程java栈的大小由-Xss参数确定),jvm则抛出OutOfMemoryError异常。例子如下:

package leon.java.outofmemory;

public class OutoffMemory {

	public static void main(String[] args) {
		int count = 0;
		while (true) {
			Thread thread = new Thread(new Runnable() {
				public void run() {
					while (true) {
						try {
							Thread.sleep(5000);
						} catch (Exception e) {	}
					}
				}
			});
			thread.start();
			System.out.println(++count);
		}
	}
}

当count达到某个值时发生:

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

 

Java堆内存溢出

Java堆用于储存对象实例。当需要为对象实例分配内存,而堆的内存占用又已经达到-Xmx设置的最大值。将会抛出OutOfMemoryError异常。例子如下:

package leon.java.outofmemory;

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

public class HeapOutoffMemory {

	public static void main(String[] args) {
		int count = 0;
		List<Object> list = new ArrayList<Object>();
		while (true) {
			list.add(new Object());
			System.out.println(++count);
		}
	}
}


count到某个值时,发生:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 

 

方法区溢出

方法区用于存放java类型的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。在类装载器加载class文件到内存的过程中,虚拟机会提取其中的类型信息,并将这些信息存储到方法区。当需要存储类信息而方法区的内存占用又已经达到-XX:MaxPermSize设置的最大值。将会抛出OutOfMemoryError异常。对于这种情况的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。这里需要借助cglib直接操作字节码运行时,生成了大量的动态类。需要导入jarcglib-2.2.2.jarasm-3.3.jar,其他版本的也可以。例子如下:

package leon.java.outofmemory;

import java.lang.reflect.Method;

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

public class MaxPermSizeOverflow {
	public static void main(String[] args) {
		int count = 0;
		while (true) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(MaxPermSizeOverflow.class);
			enhancer.setUseCache(false);
			enhancer.setCallback(new MethodInterceptor() {
				public Object intercept(Object obj, Method method,
						Object[] args, MethodProxy proxy) throws Throwable {
					return proxy.invoke(obj, args);
				}
			});
			enhancer.create();
			System.out.println(++count);
		}
	}
}


count达到某个值是,抛出:

5049

Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null

       at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)

       at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)

       at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)

       at leon.java.outofmemory.MaxPermSizeOverflow.main(MaxPermSizeOverflow.java:22)

Caused by: java.lang.reflect.InvocationTargetException

       at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)

       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

       at java.lang.reflect.Method.invoke(Method.java:597)

       at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)

       at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)

       ... 3 more

Caused by: java.lang.OutOfMemoryError: PermGen space

       at java.lang.ClassLoader.defineClass1(Native Method)

       at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)

       at java.lang.ClassLoader.defineClass(ClassLoader.java:616)

       ... 8 more

 


请注意提示信息: Caused by: java.lang.OutOfMemoryError: PermGen space

这就是方法区溢出。

了解了这个之后,对处理OutOfMemoryError的问题应该有很大的帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值