jvm中除了程序计数器其他运行时区域的OutOfMemoryError
1. java堆溢出测试
java堆用于存储对象实例,只要不断创建对象,并且保证GC Roots到对象之间的可达路径来避免垃圾回收机制清理这些对象,那么当对象的达到堆的容量最大值滞后就会产生outofmemoryerror;
通过参数:-Xms20m 最小堆大小; -Xmx20m 最大堆大小; -XX:+HeapDumpOnOutOfMemoryError 虚拟机在内存溢出异常时Dump出当前的内存堆转储快照
具体配置:
-verbose:gc -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:SurvivorRatio=8
代码:
1 package javaTest; 2 3 import java.io.Serializable; 4 import java.util.LinkedList; 5 import java.util.List; 6 7 public class Portal { 8 9 /** 10 * vm args : -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError 11 * @param args 12 */ 13 public static void main(String[] args) { 14 System.out.println("test heap memory dump"); 15 16 17 List<HeapOOM> lists = new LinkedList<HeapOOM>(); 18 19 while(true){ 20 lists.add(new HeapOOM()); 21 } 22 23 24 25 } 26 27 public static class HeapOOM implements Serializable{ 28 29 /** 30 * 31 */ 32 private static final long serialVersionUID = -7562967142125991489L; 33 34 private int count; 35 36 public int getCount() { 37 return count; 38 } 39 40 public void setCount(int count) { 41 this.count = count; 42 } 43 44 45 } 46 47 }
异常:
注意:一定要辨别是内存泄露(memory Leak)还是内存溢出(memory Overflow)
2. java虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverFlowError异常;
如果虚拟机的扩展栈无法申请到足够的内存空间,则抛出OutOfMemoryError异常;
参数:-Xoss 设置本地方法栈大小;-Xss 设置虚拟机房发展大小
具体设置:
-Xss128k
代码:
1 package javaTest; 2 3 public class StackOverFlowTest { 4 5 /** 6 * vm args : -Xss128K 7 * @param args 8 */ 9 public static void main(String[] args){ 10 11 OverFlowStackClass model = new OverFlowStackClass(); 12 13 try{ 14 model.stackLeak(); 15 }catch(Throwable ex){ 16 System.out.println("最大深度:"+String.valueOf(model.stackLength)); 17 throw ex; 18 } 19 20 21 22 23 24 25 } 26 27 static class OverFlowStackClass{ 28 public int stackLength=1; 29 30 public void stackLeak(){ 31 stackLength++; 32 stackLeak(); 33 } 34 } 35 36 37 38 }
异常:
如果是创建过多的线程导致内存溢出,在不减少线程数量或者更换64bit虚拟机的情况下,就只能通过减少最大堆和减少虚拟机栈容量来换取更多的线程,如果没有这方便的处理经验,这种通过增加线程挤占java虚拟机栈的方式来验证java栈的内存溢出(outofmemoryerror)的场景是很难想到的;
本人电脑是windows这里对于这种情况就不做演示了,代码实现其实就是疯狂的创建新的线程来实现。java栈的内存溢出;
3. 方法区和运行时常量池的内存溢出
String.intern();是一个Native方法,它的作用是 如果字符串常量池中已经包含一个等于此String对象的字符串,则返回字符串常量池中的这个字符串;否则创建一个新的对象,该对象添加到常量池中,最后返回此字符串的引用。
参数:-XX:PermSize -XX:MaxPermSize
具体参数:
-XX:PermSize=10M -XX:MaxPermSize=10M
代码:
1 package javaTest; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 public class MethodOutOfMemoryError { 7 8 /** 9 * vm args: -XX:PermSize=10M -XX:MaxPermSize=10M 10 * @param args 11 */ 12 public static void main(String[] args){ 13 14 List<String> lists = new ArrayList<String>(); 15 16 int i=0; 17 while(true){ 18 lists.add(String.valueOf(i++).intern()); 19 } 20 21 } 22 23 }
异常:
1.7+以上版本不会出现异常了,可以通过:-verbose:gc查看GC的回收过程;
4. 本机直接内存溢出
参数:-XX:MaxDirectMemorySize 制定可以使用的机器内存的大小
具体运行参数:
-Xmx10m -XX:MaxDirectMemorySize=10m
代码:
1 package javaTest; 2 3 import java.lang.annotation.Annotation; 4 import java.lang.reflect.Field; 5 6 import sun.misc.Unsafe; 7 8 public class DirectMemoryTest { 9 10 private static final int _1MB = 1024*1024; 11 12 /** 13 * vm agrs: -Xmx10m -XX:MaxDirectMemorySize=10m 14 * @param args 15 * @throws IllegalAccessException 16 * @throws IllegalArgumentException 17 */ 18 public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException{ 19 20 System.out.println(_1MB); 21 22 Field unsafeField = Unsafe.class.getDeclaredFields()[0]; 23 unsafeField.setAccessible(true); 24 Unsafe unsafe = (Unsafe)unsafeField.get(null); 25 while(true){ 26 unsafe.allocateMemory(_1MB); 27 } 28 29 30 31 32 } 33 }
异常:
虽然java有垃圾回收机制,但是内存溢出对我们来说也是家常便饭,所以要明白什么情况下导致内存溢出,从而尽量避免内存溢出的发生;
如果出现内存异常根据堆栈信息可以快速排查是什么原因;