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. sqlite、mysql 将时间戳转换成本地时间语句

    sqlite:SELECT datetime(createdTime, 'unixepoch', 'localtime'); mysql:SELECT FROM_UNIXTIME( 124948800 ...

  2. webapi 权限控制解决方案

    随着移动互联网的发展,webapi的应用越来越广泛,本文是笔者总结的webapi的认证校验案例,欢迎指出 案例分为两个功能: 1.用户登录,传入账号和密码到api服务器,然后服务器使用FormsAut ...

  3. Silverlight & Blend动画设计系列六:动画技巧(Animation Techniques)之对象与路径转化、波感特效

    当我们在进行Silverlight & Blend进行动画设计的过程中,可能需要设计出很多效果不一的图形图像出来作为动画的基本组成元素.然而在设计过程中可能会出现许多的问题,比如当前绘制了一个 ...

  4. Node.js学习笔记(三) --- package.json 及cnpm

    一.包 Nodejs   中除了它自己提供的核心模块外,我们可以自定义模块,也可以使用第三方的模块.Nodejs 中第三方模块由包组成,可以通过包来对一组具有相互依赖关系的模块进行统一管理. 完全符合 ...

  5. DataTables获取指定元素的行数据

    法1: 用jquey获取,var row = $('.edit').parent().parent(); 缺点:只能获取dom上的东西,不能获取没有渲染的数据 法2: 首先绑定行号到元素上 $('#e ...

  6. 深入理解ES6之函数

    一:关于函数的参数: 可以接受任意数量的参数而无视函数声明的参数数量是js函数的独特之处. 1:参数默认值 ES6之前做法: function makeRequest(url,timeout,call ...

  7. js-JavaScript的简介

    JavaScript的简介 * 是基于对象和事件驱动的语言,应用于客户端 - 基于对象: ** 提供好了很多对象,可以直接拿过来使用 - 事件驱动: ** HTML做网站静态效果,JavaScript ...

  8. js 密码 正则表达式

    1. 代码 function checkPassword(str){ var reg1 = /[!@#$%^&*()_?<>{}]{1}/; var reg2 = /([a-zA- ...

  9. sublime_key 快捷键

    1.Ctrl+H :查找替换 2.Ctrl+D :选择游标所在单词,连续Ctrl+D 实现多行选择(选择与第一次选择相同的单词) 3.Ctrl+K Ctrl+D 跳过当前选择,选择下一个 4.Ctrl ...

  10. UNIX/Linux系统管理技术手册(2)----bash脚本编程

    1. 一个简单的例子: $ vim readname.sh #file:readname.sh#!/bin/bash echo -n "Enter your name: " rea ...