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