JVM探秘2--详解内存溢出OutOfMemoryError异常

JVM运行时内存被划分成多个区域,而除了程序计数器之外,其他几个区都会出现OutOfMemoryError异常,主要原因就是对应内存区域的内存不足以再分配内存,一般要么是内存泄漏了要么就是内存参数设置的过小而导致。本文就在实际操作中模拟下JVM内存模型中各个区域出现内存溢出的场景。

 

1.堆内存溢出

先设置JVM启动参数,设置初始化堆内存大小为 -Xms15M  -Xmx15M

 堆内存中主要存储对象实例,所以测试堆内存溢出就需要不断的创建对象实例,并且保证这些对象实例不被垃圾回收,测试代码如下

 1 public static void main(String[] args)
 2     {
 3         heapErrorTest();
 4     }
 5     
 6     public static void heapErrorTest()
 7     {
 8         List<User> userList = new ArrayList<>();
 9         int i = 0;
10         try
11         {
12             while (true)
13             {
14                 i++;
15                 System.out.println(i);
16                 userList.add(new User());
17             }
18         }
19         catch (Exception e)
20         {
21             System.out.println("成功创建user对象个数为:"+i);
22             e.printStackTrace();
23         }
24     }

 运行结果如下示:

1 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
2     at java.util.Arrays.copyOf(Arrays.java:3210)
3     at java.util.Arrays.copyOf(Arrays.java:3181)
4     at java.util.ArrayList.grow(ArrayList.java:261)
5     at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
6     at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
7     at java.util.ArrayList.add(ArrayList.java:458)
8     at com.lucky.test.jvmtest.JVMTest.heapErrorTest(JVMTest.java:27)
9     at com.lucky.test.jvmtest.JVMTest.main(JVMTest.java:14)

 

此时报了OutOfMemoryError异常,也提示了是Java堆内存空间报的,但是是无法确定是什么原因导致的,也不知道具体是什么对象导致的,测试用例比较简单,而复杂的线上环境中就很难排查,所以需要让JVM在出现内存溢出的时候Dump出当前的内存堆转储快照,当出现异常后就可以使用其他工具来进行分析。针对上例,修改启动参数,添加 -XX: +HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=C:/Users/admin/Desktop/jvmdump/heapdump.hprof

运行结果为:

 1 java.lang.OutOfMemoryError: Java heap space
 2 Dumping heap to C:/Users/admin/Desktop/jvmdump/heapdump.hprof ...
 3 Heap dump file created [24643218 bytes in 0.071 secs]
 4 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 5     at java.util.Arrays.copyOf(Arrays.java:3210)
 6     at java.util.Arrays.copyOf(Arrays.java:3181)
 7     at java.util.ArrayList.grow(ArrayList.java:261)
 8     at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
 9     at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
10     at java.util.ArrayList.add(ArrayList.java:458)
11     at com.lucky.test.jvmtest.JVMTest.heapErrorTest(JVMTest.java:27)
12     at com.lucky.test.jvmtest.JVMTest.main(JVMTest.java:14)

 

分析dump文件可以使用Eclipse的Memory Analysis插件,安装过程是打开Eclipse Help-》Eclipse Marketplace  然后搜索memory,然后直接安装即可

 

2.虚拟机栈内存溢出和栈溢出

虚拟机栈有栈溢出和内存溢出两种异常情况,栈溢出(StackOverFlowError)是在栈深度大于虚拟机设置的最大深度时会报出,内存溢出则会在内存不足时报出

栈溢出案例代码如下:

1  public static void stackOverFlowErrorTest()
2     {
3         // 栈溢出栈的深度过长导致,可以用递归方法在模拟
4         System.out.println("当前深度为:" + (i++));
5         stackOverFlowErrorTest();
6     }

 

运行结果为:

当前深度为:7958
当前深度为:7959
当前深度为:7960
当前深度为:7961
当前深度为:7962
当前深度为:7963
Exception in thread "main" java.lang.StackOverflowError
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
    at java.lang.StringBuilder.append(StringBuilder.java:136)
    at java.lang.StringBuilder.<init>(StringBuilder.java:113)
    at com.lucky.test.jvmtest.JVMTest.stackOverFlowErrorTest(JVMTest.java:43)
    at com.lucky.test.jvmtest.JVMTest.stackOverFlowErrorTest(JVMTest.java:44)
    at com.lucky.test.jvmtest.JVMTest.stackOverFlowErrorTest(JVMTest.java:44)

 

内存溢出模拟案例如下:

 1 public static void stackOutOfMemoryTest(){
 2         while(true){
 3         new Thread(new Runnable()
 4         {
 5             
 6             @Override
 7             public void run()
 8             {
 9                 dontStop();
10             }
11         }).start();
12         }
13     }
14     
15     public static void dontStop(){
16         while(true){
17             System.out.println(Thread.currentThread().getName());
18         }
19     }

 

通过不停的创建新线程,执行一个执行不完的方法,当到达一定数量时,内存就会不足以创建新线程。

结果为:   java.lang.OutOfMemoryError: Java heap space

4.方法区内存溢出

方法区主要存放Class信息、以及静态常量,正常情况下方法区存储的内容是很少变动的,因为都是静态的内容,所以出现方法区内存溢出的情况也很少,但是有很多主流框架如Spring、Hibernate框架会对类进行增强,通过直接操作字节码,会生成大量的动态类,这时就会有大量的动态类需要存储在方法区,如果方法区的内存不足,也会出现内存溢出的情况。先设置JVM方法区内存大小 -XX:PermSize=10M -XX:MaxPermSize=10M

案例代码如下:

 1 public static void permGenErrorTest(){
 2         while(true){
 3             Enhancer enhancer = new Enhancer();
 4             enhancer.setSuperclass(User.class);
 5             enhancer.setUseCache(false);
 6             enhancer.setCallback(new MethodInterceptor()
 7             {
 8                 
 9                 @Override
10                 public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3)
11                     throws Throwable
12                 {
13                     return arg3.invokeSuper(arg0, arg2);
14                 }
15             });
16             enhancer.create();
17         }
18     }

 

最终抛出异常:Caused by:java.lang.OutOfMemoryError:PermGen Space 

转载于:https://www.cnblogs.com/jackion5/p/10629584.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值