1.前言

上一篇我们介绍了java的内存区域结构,这一篇,模拟内存溢出的几个场景,下面一个图是总体的指导思想:

2.Java堆溢出

Java堆唯一的作用就是存储对象实例,只要保证不断创建对象并且对象不被回收,那么对象数量达到最大堆容量限制后就会产生内存溢出异常了。所以测试的时候把堆的大小固定住并且让堆不可扩展即可。测试代码如下:

 1 package com.xrq.test;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * 测试内容:堆溢出
8 *
9 * 虚拟机参数:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
10 */
11 public class HeapOverflowTest
12 {
13 public static void main(String[] args)
14 {
15 List<HeapOverflowTest> list = new ArrayList<HeapOverflowTest>();
16 while (true)
17 {
18 list.add(new HeapOverflowTest());
19 }
20 }
21 }

运行结果

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid8876.hprof ...
Heap dump file created [15782068 bytes in 0.217 secs]
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 com.xrq.test.HeapOverflowTest.main(HeapOverflowTest.java:18)

这种异常很常见,也很好发现,因为都提示了“Java heap space”了,定位问题的话,根据异常堆栈分析就好了,行号都有指示。

解决方案:可以调大堆的大小或者从代码上检视是否存在某些对象生命周期过长、持有状态时间过长的情况,长时间减少程序运行期间的内存消耗。

另外,由于Java堆内也可能发生内存泄露(Memory Leak),这里简要说明一下内存泄露和内存溢出的区别:

内存泄露:是指分配出去的内存没有被回收回来,由于失去了对该内存区域的控制,因而造成了资源的浪费。Java中一般不会产生内存泄露,因为有垃圾回收器自动回收垃圾,但这也不绝对,当我们new了对象,并保存了其引用,但是后面一直没用它,而垃圾回收器又不会去回收它,这边会造成内存泄露,

内存溢出:是指程序所需要的内存超出了系统所能分配的内存(包括动态扩展)的上限。

3.方法区和运行时常量池溢出

运行时常量池也是方法区的一部分,所以这两个区域一起看就可以了。这个区域的OutOfMemoryError可以利用String.intern()方法来产生。这是一个Native方法,意思是如果常量池中有一个String对象的字符串就返回池中的这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中去,并且返回此String对象的引用。测试代码如下:

 1 package com.xrq.test;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * 测试内容:常量池溢出(这个例子也可以说明运行时常量池为方法区的一部分)
8 *
9 * 虚拟机参数-XX:PermSize=10M -XX:MaxPermSize=10M
10 */
11 public class ConstantPoolOverflowTest
12 {
13 public static void main(String[] args)
14 {
15 List<String> list = new ArrayList<String>();
16 int i = 0;
17 while (true)
18 {
19 list.add(String.valueOf(i++).intern());
20 }
21 }
22 }

运行结果

Exception in thread "Reference Handler" Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at com.xrq.test.ConstantPoolOverflowTest.main(ConstantPoolOverflowTest.java:19)
java.lang.OutOfMemoryError: PermGen space
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:123)

之前有讲过,对于HotSpot而言,方法区=永久代,这里看到OutOfMemoryError的区域是“PermGen space”,即永久代,那其实也就是方法区溢出了。注意一下JDK1.7下是不会有这个异常的,while循环将一直下去,因为JDK1.7之后溢出了永久代并采用Native Memory来实现方法区的规划了

4.栈溢出

Java虚拟机规范中描述了如果线程请求的栈深度太深(换句话说方法调用的深度太深),就会产生栈溢出了。那么,我们只要写一个无限调用自己的方法,自然就会出现方法调用的深度太深的场景了。测试代码如下

 1 package com.xrq.test;
2
3 /**
4 * 测试内容:栈溢出测试(递归调用导致栈深度不断增加)
5 *
6 * 虚拟机参数:-Xss128k
7 */
8 public class StackOverflowTest
9 {
10 private int stackLength = 1;
11
12 public void stackLeak()
13 {
14 stackLength++;
15 stackLeak();
16 }
17
18 public static void main(String[] args) throws Throwable
19 {
20 StackOverflowTest stackOverflow = new StackOverflowTest();
21 try
22 {
23 stackOverflow.stackLeak();
24 }
25 catch (Throwable e)
26 {
27 System.out.println("stack length:" + stackOverflow.stackLength);
28 throw e;
29 }
30 }
31 }

运行结果:

stack length:1006
Exception in thread "main" java.lang.StackOverflowError
at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:14)
at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
  ...

多线程:这里需要注意一下多线程的情况,多线程下通过不断创建线程的方式,可以产生OutOfMemoryError异常,因为每个线程都有自己的栈空间,栈空间越大,越容易产生内存溢出异常。其实这也很好理解,操作系统分配给进程的内存是有限制的,比如32位的Windows限制为2GB。虚拟机提供了了参数来控制Java堆和方法区这两部分内存的最大值,剩余内存为2GB-最大堆容量-最大方法区容量,程序计数器很小就忽略了,虚拟机进程本身的耗费也不算,剩下的内存就是栈的了。每个线程分配到的栈容量越大,可建立的线程数自然就越少,建立线程时就越容易把剩下的内存耗尽,可能造成操作系统的假死。

多线程导致的OutOfMemoryError,在不能减少线程数的情况下,就只能通过减少最大堆和每个线程的栈容量来换取更多的线程了。

单线程:StackOverFlowError这个异常,有错误堆栈可以阅读,比较好定位。而且如果使用虚拟机默认参数,栈深度在大多数情况下,达到1000~2000完全没有问题,正常方法的调用这个深度应该是完全够了。

转载地址:http://www.cnblogs.com/xrq730/p/4833713.html

http://blog.csdn.net/ns_code/article/details/17565503

Java虚拟机3:内存溢出的更多相关文章

  1. 深入理解java虚拟机【内存溢出实例】

    通过简单的小例子程序,演示java虚拟机各部分内存溢出情况: (1).java堆溢出: Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收 ...

  2. 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

  3. java虚拟机涉及内存溢出

    Java语言写的代码是.java文件,它会被特定程序编译(javac.exe,它会被Eclipse之类的IDE调用)成字节码(bytecode),字节码不能直接在CPU上运行,需要另一个程序读取并执行 ...

  4. Java 虚拟机的内存溢出

    在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError 异常的可能. 在Eclipse中进行JVM参数设置 可以直接通过上方菜单栏的 ...

  5. java虚拟机(四)--内存溢出、内存泄漏、SOF

    学习了java运行时数据区,知道每个内存区域保存什么数据,可以参考:https://www.cnblogs.com/huigelaile/p/diamondshine.html,然后了 解内存溢出和内 ...

  6. 【java虚拟机】内存溢出与内存泄漏

    作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/7354857.html 一.基本概念 内存溢出:简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提 ...

  7. 【java虚拟机】内存溢出解决思路

    转自:https://blog.csdn.net/u013521220/article/details/79523633 内存溢出与数据库锁表的问题,可以说是开发人员的噩梦,一般的程序异常,总是可以知 ...

  8. (一)深入java虚拟机之内存溢出与分析

    一.内存溢出程序 public class Test { public static void main(String[] args) { List<User> userList=new ...

  9. 实战Java虚拟机之一“堆溢出处理”

    从今天开始,我会发5个关于java虚拟机的小系列: 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实战Java虚拟机之四 ...

  10. 从Java虚拟机的内存区域、垃圾收集器及内存分配原则谈Java的内存回收机制

    一.引言: 在Java中我们只需要轻轻地new一下,就可以为实例化一个类,并分配对应的内存空间,而后似乎我们也可以不用去管它,Java自带垃圾回收器,到了对象死亡的时候垃圾回收器就会将死亡对象的内存回 ...

随机推荐

  1. hibernate 学习笔记3

    1. 多对多关联: 在双方都要用一个类型为Set的属性保存对方的信息,并在映射配置文件中指定这个属性的名字,并指定中间表.还需要通过<key column=””>来指定自己在中间表中对应的 ...

  2. Windows加密服务架构

    Windows加密是安全体系的重要基础和组成部分.现代CPU的保护模式是系统安全的硬件基石,基于CPU硬件的特权分级,Windows让自身的关键系统代码运行在高处理器特权级的内核模式,各种应用程序则运 ...

  3. vue基础知识之vue-resource/axios

    Vue基础知识之vue-resource和axios(三)   vue-resource Vue.js是数据驱动的,这使得我们并不需要直接操作DOM,如果我们不需要使用jQuery的DOM选择器,就没 ...

  4. 关于vue的常识问题及解决方法

    一.VSCode开发必备插件 1.Beautify:语法高亮: 2.Bracket Pair Colorizer :对括号对进行着色: 3.ESLint:ESLint插件,高亮提示: 4.HTML C ...

  5. IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述

    本主题概述 ASP.NET 应用程序的生命周期,列出了重要的生命周期事件,并描述了您编写的代码将如何适应于应用程序生命周期.本主题中的信息适用于 IIS 5.0 和 IIS 6.0.有关 IIS 7. ...

  6. nginx 代理转发 wcf接口

    前言 以前对比过enginx和其他几个web服务器(IIS,Apache,lighttpd)的处理静态文件的能力,enginx是最好的,甚至超过其他的几倍. 虽说enginx官方声明在Windows上 ...

  7. Firebird with lock

    Firebird 锁,默认是行级锁,即记录锁. 通常最常用的是 with lock ,即:将查出的所有记录都锁定,但允许其他事务读取,不允许其他事务更新.删除.本事务允许更新. 另一种 for upd ...

  8. JQuery对数组的一些操作总结

    JQuery对数组的处理非常便捷并且功能强大齐全,一步到位的封装了很多原生js数组不能企及的功能.下面来看看JQuery数组的强大之处在哪. $.each(array, [callback]) 遍历 ...

  9. mysql表情存储报错问题

    mysql采用utf-8字符编码,但在移动端使用输入法的表情并存储数据库的时候,出现错误. java.sql.SQLException: Incorrect string value: '\xF0\x ...

  10. Effective C++ .17 函数调用时的资源管理

    以书上的代码为例 processWidget(shared_ptr<Widget>(new Widget), priority()) 虽然使用了智能指针来管理资源但是,由于参数值计算顺序的 ...