Java虚拟机2——Java内存溢出实例

本文通过具体示例展示了Java虚拟机不同内存区域发生溢出的情况,包括堆溢出、栈溢出、运行时常量池溢出、方法区溢出及本机直接内存溢出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过简单的小例子程序,演示java虚拟机各部分内存溢出情况:

(1)java堆溢出:

java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量是产生OutOfMemoryError异常。

想要方便快速地产生堆溢出,要使用如下java虚拟机参数:-Xms 10m(最小堆内存为10MB),-Xmx10m(最大堆内存为10M,最小堆内存和最大堆内存相同是为了避免堆动态扩展),-XX: +HeapDumpOutOfMemoryError可以让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());

        }

    }

}

运行一段时间就会发现产生OutOfMemoryError异常,并且产生了堆内存异常dump文件。

(2)java虚拟机栈和本地方法栈溢出:

由于Sun的HotSpot虚拟机不区分java虚拟机栈和本地方法栈,因此对于HotSpot虚拟机来说-Xoss参数(设置本地方法栈大小)虽然存在,但是实际上是无效的,栈容量只能由-Xss参数设定。

由于Java虚拟机栈会出现StackOverflowError和OutOfMemoryError两种异常,所以分别使用两个例子演示这两种情况:

a、java虚拟机栈深度溢出:

单线程的环境下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机总抛出StackOverflowError异常。使用-Xss128k将java虚拟机栈大小设置为128kb,例子代码如下:

public class JavaVMStackOF{

    private int stackLenth =1;

    public void stackLeak(){      

        stackLength++;

         stackLeak();

    }

   public static void main(String[] args){

        JavaVMStackOF oom = new JavaVMStackOF();

         oom.stackLeak();

   }

}

运行一段时间后,产生StackOverflowError异常。Java虚拟机栈溢出一般会产生在方法递归调用过多而java虚拟机栈内存不够的情况下。

b、java虚拟机栈内存溢出:

多线程环境下,能够创建的线程最大内存=物理内存-最大堆内存-最大方法区内存,在java虚拟机栈内存一定的情况下,单个线程占用的内存越大,所能创建的线程数目越小,所以在多线程条件下很容易产生java虚拟机栈内存溢出的异常。

使用-Xss2m参数设置java虚拟机栈内存大小为2MB,例子代码如下:

public class JavaVMStackOOM{

    private void dontStop(){

           while(true){

          }

   }

   public void stackLeakByThread(){

        while(true){

              Thread t = new Thread(new Runnable(){

                public void run(){

                   dontStop(){

                 }

    });

   t.start();

 }

}

public static void main(String[] args){

         JavaVMStackOOM oom = new JavaVMStackOOM();

         oom.stackLeakByThread();

   }

}

运行一段时间之后,java虚拟机栈就会因为内存太小无法创建线程而产生OutOfMemoryError。

(3)运行时常量池溢出:

运行时常量池属于方法区的一部分,可以使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最大内存设置为10Mb大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。

String的intern()方法用于检查常量池中如果有等于此String对象的字符串存在,则直接返回常量池中的字符串对象,否则,将此String对象所包含的字符串添加到运行时常量池中,并返回此String对象的引用。因此String的intern()方法特别适合演示运行时常量池溢出,例子代码如下:

public class RuntimeConstantPoolOOM{

       public static void main(String[] args){

           List<String> list = new ArrayList<String>();

           int i=0;

           while(true){

                list.add(String.valueOf(i++).intern());

           }

    }

}

运行一段时间,永久代内存不够,运行时常量池因无法再添加常量而产生OutOfMemoryError。

(4)方法区溢出:

运行时常量池是方法区的一部分,他们都属于HotSpot虚拟机中的永久代内存区域。方法去用于存放Class的相关信息,Java的反射和动态代理可以动态产生Class,另外第三方的CGLIB可以直接操作字节码,也可以动态产生Class,实验通过CGLIB来演示,同样使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。例子代码如下:

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();

            }

     }

     class OOMObject{

        }

  }

运行一段时间之后,永久代内存不够,方法去无法再存放CGLIB创建处理的Class信息,产生方法区OutOfMemoryError。

(5)本机直接内存溢出:

java虚拟机可以通过参数-XX:MaxDirectMemorySize设定本机直接内存可用大小,如果不指定,则默认与java堆内存大小相同。JDK中可以通过反射获取Unsafe类(Unsafe的getUnsafe()方法只有启动类加载器Bootstrap才能返回实例)直接操作本机直接内存。通过使用-XX:MaxDirectMemorySize=10M,限制最大可使用的本机直接内存大小为10Mb,例子代码如下:

public class DirectMemoryOOM{

    private static final int _1MB = 1024 * 1024 * 1024;

     public static void main(String[] args) throws Exception{

          Field unsafeField = Unsafe.class.getDeclaredFields()[0];

         unsafeField.setAccessible(true);

         Unsafe unsafe = (Unsafe) unsafeField.get(null);

          while(true){

              //unsafe直接向操作系统申请内存

              unsafe.allocateMemory(_1MB);

         }

   }

}

当运行一段时间之后,10MB的本机直接内存被分配光,无法再进行直接内存分配时,产生OutOfMemoryError。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值