Learning JVM 03 OutOfMemoryError异常

Java堆溢出

发生情况

Java堆用于存储对象实例,只要不断创建对象,而垃圾回收机制没有即时清除这些对象,对象数量达到最大堆的数量限制后产生内存溢出异常。

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.lang.OutOfMemoryError Java heap space

如何解决

Eclipese Memory Analyzer
内存泄漏(Memory Leak):程序在申请内存后,无法释放已申请的内存空间。一次内存泄漏危害可以忽略,但内存泄漏积累会迟早耗光内存。
内存溢出(Memory Overflow):程序在申请内存时,没有足够的内存空间供其使用。要求分配的内存超过了系统能给你的。
参考资料:
内存溢出和内存泄漏的区别、产生原因以及解决方案

虚拟机栈和本地方法栈溢出

线程请求的栈深度大于虚拟机所允许的最大深度——StackOverFlowError异常。不断的递归调用。
虚拟机扩展栈时无法申请到足够的内存空间——OutOfMemoryError异常。不断创建新的线程。
分成两种看似严谨,其实在说一件事:已使用的栈空间太大了,or内存空间太小了。
前一种通过不断的递归调用。在单个线程下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法分配时,就会抛出StackOverFlowError:

public class StackSOF{
	private int stackLength =1;
	public void stackLeak(){
		stackLength++;
		stackleak();
	}
	public static void main(String[] args) throws Throwable {
		StackOOM stackOverFlow = new StackSOF();
		try{
			stackOverFlow.stackLeak();
		}catch(Throwable e){
			System.out.println("stack length:"+stackOverFlow.stackLength);
			throw e
		}
	}
}

异常:java.lang.StackOverflowError

后一种通过不断的建立线程。操作系统分配给每个进程的内存是有限的。
最大堆容量+最大方法区容量+进程内存=操作系统限制
每个线程分配到的栈容量越大,可以建立的线程容量自然也更少。
建立多线程所导致的内存溢出,在不能减少线程数或者增大操作系统内存的情况下,就只能通过减小最大堆和栈容量来获取更多的线程。

public class StackOOM{
		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) throw Throwable(){
			StackOOM outofmemory=new StackOOM();
			outofmemory=
		}
}

异常:java.lang.OutOfMemory: unable to create new native thread.

运行时常量池溢出

产生大量的字符串对象。
一个Native方法——String.intern():如果常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。

String的valueOf()函数:
valueOf() 方法有以下几种不同形式:
valueOf(boolean b): 返回 boolean 参数的字符串表示形式。.
valueOf(char c): 返回 char 参数的字符串表示形式。
valueOf(char[] data): 返回 char 数组参数的字符串表示形式。
valueOf(char[] data, int offset, int count): 返回 char 数组参数的特定子数组的字符串表示形式。
valueOf(double d): 返回 double 参数的字符串表示形式。
valueOf(float f): 返回 float 参数的字符串表示形式。
valueOf(int i): 返回 int 参数的字符串表示形式。
valueOf(long l): 返回 long 参数的字符串表示形式。
valueOf(Object obj): 返回 Object 参数的字符串表示形式。

“A native method is a Java method whose implementation is provided by non-java code.”

public class RuntimeConstantPoolOOM{
		public static void main (String[] args){
			List<Interger> list = new List<Integer>();
			int i= 0;
			while(true){
				list.add(String.valueOf(i++).intern());
			}
		} 
}

异常:java.lang.OutOfMemoryError:PermGen space

方法区溢出

运行时产生大量的类填满方法区。
方法区存放Class的相关信息,如类名、访问变量符、常量符、字段描述、方法描述。
CGlib字节码技术。

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{
	}
}

异常:java.lang.OutOfMemoryError: PermGen space

本机直接内存溢出

DirectMemory容量默认与Java堆的最大值一样。
向操作系统申请分配内存的方法:unsafe.allocateMemory()。

public class DirectMemoryOOM{
	private static final int _1MB= 1024*1024;
	public static void main(String[] args){
		Field unsafeField= Unsafe.class.getDeclaredFields()[0];
		unsafeField.setAccessible(true);
		Unsafe unsafe=(Unsafe) unsafeField.get(null);
		while(true){
			unsafe.allocateMemory(_1MB);
		}
	}
}

异常:java.lang.OutOfMemoryError

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值