做Java程序开发,难免会遇到OutOfMemory,导致的原因也是不尽相同,下面我们来捋一捋OOM出现的场景。
一,堆空间不足,这是最容易,也是最常见的OOM。了解Java内存结构的同学应该知道,Java里面创建的对象大部分都是位于堆空间的,当创建的对象太多,而堆空间不足时,很容易抛出OOM,如下:
运行结果:
二,直接分配内存溢出,Java提供了一些可以直接操作内存和线程的低层次操作(native)-sun.misc.Unsafe,其被JDK广泛用于自己的包中,如java.nio和java.util.concurrent。
但是丝毫不建议在生产环境中使用这个Unsafe,从名字就可以看出,这个API十分不安全、不轻便、而且不稳定。
运行结果:
三,方法区溢出,方法区主要存放类的信息、静态变量、Field、Method信息等,当不停地有类动态创建并加载时,方法区也能产生OOM。
运行结果:
四,常量池溢出,常量池属于方法区,存放一些常量值(如static final),还有一些文本形式出现的符号引用,如:类和接口的全限定名、字段的名称和描述符。
运行结果:
五,Stack溢出,JVM方法栈为线程私有,当方法执行时会被创建,当方法执行完毕,其对应的栈帧所占用的内存也会被自动释放。
当方法栈的深度不足时,会抛出StackOverflowError,不过只要不出现无穷递归,栈的深度不会太大。
运行结果:
补充:
一,堆空间不足,这是最容易,也是最常见的OOM。了解Java内存结构的同学应该知道,Java里面创建的对象大部分都是位于堆空间的,当创建的对象太多,而堆空间不足时,很容易抛出OOM,如下:
点击(此处)折叠或打开
- import java.util.ArrayList;
- import java.util.List;
-
- /*VM args:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
- *
- * -Xms:堆的最小值;-Xmx:堆的最大值
- *
- * */
- public class HeapOOM {
-
- static class OOMObject {
- }
-
- public static void main(String[] args) {
- List<OOMObject> list = new ArrayList<OOMObject>();
- while (true) {
- list.add(new OOMObject());
- }
- }
- }
点击(此处)折叠或打开
- Exception in thread \"main\" java.lang.OutOfMemoryError: Java heap space
- at java.util.Arrays.copyOf(Arrays.java:2760)
- at java.util.Arrays.copyOf(Arrays.java:2734)
- at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
- at java.util.ArrayList.add(ArrayList.java:351)
- at test.java.VM.OOM.HeapOOM.main(HeapOOM.java:19)
二,直接分配内存溢出,Java提供了一些可以直接操作内存和线程的低层次操作(native)-sun.misc.Unsafe,其被JDK广泛用于自己的包中,如java.nio和java.util.concurrent。
但是丝毫不建议在生产环境中使用这个Unsafe,从名字就可以看出,这个API十分不安全、不轻便、而且不稳定。
点击(此处)折叠或打开
- import java.lang.reflect.Field;
-
- import sun.misc.Unsafe;
-
- /*VM args:-Xmx10m -XX:MaxDirectMemorySize=5m
- *
- * */
- public class DirectMemoryOOM {
- private static final int _1MB = 1024 * 1024;
-
- public static void main(String[] args) throws Exception {
- Field unsafeField = Unsafe.class.getDeclaredField(\"theUnsafe\");
- 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 test.java.VM.OOM.DirectMemoryOOM.main(DirectMemoryOOM.java:19)
三,方法区溢出,方法区主要存放类的信息、静态变量、Field、Method信息等,当不停地有类动态创建并加载时,方法区也能产生OOM。
点击(此处)折叠或打开
- import java.lang.reflect.Method;
-
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
-
- /*VM args: -XX:PermSize=5m -XX:MaxPermSize=5m
- *
- * */
- 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() {
- @Override
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- //return method.invoke(obj, args);
- return proxy.invokeSuper(obj, args);
- }
- });
- OOMObject object = (OOMObject) enhancer.create();
- }
-
- }
-
- static class OOMObject {
- }
- }
点击(此处)折叠或打开
- Exception in thread \"main\" java.lang.OutOfMemoryError: PermGen space
四,常量池溢出,常量池属于方法区,存放一些常量值(如static final),还有一些文本形式出现的符号引用,如:类和接口的全限定名、字段的名称和描述符。
点击(此处)折叠或打开
- import java.util.ArrayList;
- import java.util.List;
-
- /*VM rags:-XX:PermSize=5m -XX:MaxPermSize=5m
- *
- *
- * */
- public class RuntimeConstantPoolOOM {
- public static void main(String[] args) {
- //使用List保持着常量池引用,避免Full GC回收常量池行为
- List<String> list = new ArrayList<String>();
-
- int i = 0;
- while (true) {
- list.add(String.valueOf(i++).intern());//将String对象加入常量池
- }
- }
- }
点击(此处)折叠或打开
- Exception in thread \"main\" java.lang.OutOfMemoryError: PermGen space
- at java.lang.String.intern(Native Method)
- at test.java.VM.OOM.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:17)
- Exception in thread \"Reference Handler\" java.lang.OutOfMemoryError: PermGen space
- at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:123)
五,Stack溢出,JVM方法栈为线程私有,当方法执行时会被创建,当方法执行完毕,其对应的栈帧所占用的内存也会被自动释放。
当方法栈的深度不足时,会抛出StackOverflowError,不过只要不出现无穷递归,栈的深度不会太大。
点击(此处)折叠或打开
- /*VM args:-Xss128k
- *
- * 在单线程下,无论是由于栈帧太大,还是虚拟机容量太小,
- * 当内存无法分配的时候,虚拟机抛出的都是StackOverflowError
- * 如果测试不限于单线程,通过不断创建线程的方式倒是可以产生内存溢出异常(详见JavaVMStackOF2)
- * */
- public class JavaVMStackOF {
- private int stackLength = 1;
-
- public void stackLeak() {
- stackLength++;//其实,即使没有操作数,也会throw StackOverflowError
- stackLeak();
- }
-
- public static void main(String[] args) throws Throwable {
- JavaVMStackOF oom = new JavaVMStackOF();
- try {
- oom.stackLeak();
- } catch (Throwable e) {
- System.out.println(\"stack length:\" + oom.stackLength);
- throw e;
- }
- }
- }
点击(此处)折叠或打开
- stack length:1007Exception in thread \"main\" java.lang.StackOverflowError
-
- at test.java.VM.OOM.JavaVMStackOF.stackLeak(JavaVMStackOF.java:13)
- at test.java.VM.OOM.JavaVMStackOF.stackLeak(JavaVMStackOF.java:14)
补充:
点击(此处)折叠或打开
- /*VM args:-Xss10m
- *
- * 通过不断创建新线程达到OutOfMemoryError
- * 物理内存-Xmx(最大堆容量)-MaxPermSize(最大方法区容量)=虚拟机栈+本地方法栈,程序计数器消耗内存较小,可以忽略。
- * -Xss10m,分配给每个线程的内存。所以-Xss越大,越容易出现OutOfMemoryError(可创建的线程越少)。
- *
- * 栈深度在虚拟机默认情况下,达到1000~2000完全没问题,对于正常的方法调用(包括递归),这个深度完全够用了。
- * 但是,如果是建立过多线程导致 的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆容量和减少栈容量来换取更多的线程。
- * 如果没有这方面的经验,这种通过“减少内存”的手段来解决内存溢出的方式会比较难以想到!
- * */
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/28912557/viewspace-1455299/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/28912557/viewspace-1455299/