一、背景知识

1、JVM体系结构

2、JVM运行时数据区

3、JVM内存模型

JVM运行时内存 = 共享内存区 + 线程内存区

3-1、共享内存区

共享内存区 = 持久带 + 堆

持久带 = 方法区 + 其他

堆 = Old Space + Young Space

Young Space = Eden + S0 + S1

3-1-1、持久代

JVM用持久带(Permanent Space)实现方法区,主要存放所有已加载的类信息,方法信息,常量池等等。

可通过-XX:PermSize和-XX:MaxPermSize来指定持久带初始化值和最大值。

Permanent Space并不等同于方法区,只不过是Hotspot JVM用Permanent Space来实现方法区而已,有些虚拟机没有Permanent Space而用其他机制来实现方法区。

3-1-2、堆

堆(heap),主要用来存放类的对象实例信息(包括new操作实例化的对象和定义的数组)。

堆分为Old Space(又名,Tenured Generation)和Young Space。

Old Space主要存放应用程序中生命周期长的存活对象;

Eden(伊甸园)主要存放新生的对象;

S0和S1是两个大小相同的内存区域,主要存放每次垃圾回收后Eden存活的对象,作为对象从Eden过渡到Old Space的缓冲地带(S是指英文单词Survivor Space)。

堆之所以要划分区间,是为了方便对象创建和垃圾回收,后面垃圾回收部分会解释。

3-2、线程内存区

线程内存区=单个线程内存+单个线程内存+.......

单个线程内存=PC Regster+JVM栈+本地方法栈

JVM栈=栈帧+栈帧+.....

栈帧=局域变量区+操作数区+帧数据区

在Java中,一个线程会对应一个JVM栈(JVM Stack),JVM栈里记录了线程的运行状态。

JVM栈以栈帧为单位组成,一个栈帧代表一个方法调用。栈帧由三部分组成:局部变量区、操作数栈、帧数据区。

二、堆溢出

堆(Heap)是Java存放对象实例的地方。

堆溢出可以分为以下两种情况,这两种情况都会抛出OutOfMemoryError:java heap space异常:

1、内存泄漏

内存泄漏是指对象实例在新建和使用完毕后,仍然被引用,没能被垃圾回收释放,一直积累,直到没有剩余内存可用。

如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。

分析内存泄漏的工具有:Jprofiler,visualvm等。

示例代码:

packagecom.jvm;importjava.util.ArrayList;importjava.util.List;importjava.util.UUID;/*** 内存泄漏

*@authorfeizi

* @time 2015-1-23上午8:42:53*/

public classOOMTest {public static voidmain(String[] args) {

List list = new ArrayList();while(true){

list.add(UUID.randomUUID());

}

}

}

看看控制台的输出结果,因为我这边的JVM设置的参数内存足够大,所以需要等待一定的时间,才能看到效果:

如果是用CMD命令行,就可以自己指定参数编译运行了,这样效果就更快一些:

通过下列命令运行程序,注意先要用javac命令将.java源文件编译成.class类字节码文件。

java -Xms10M -Xmx10M -XX:-UseGCOverheadLimit OOMTest

2、内存溢出

内存溢出是指当我们新建一个实力对象时,实例对象所需占用的内存空间大于堆的可用空间。

如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。

示例代码:

packagecom.jvm;importjava.util.ArrayList;importjava.util.List;/*** 内存溢出

*@authorfeizi

* @time 2015-1-23上午8:56:22*/

public classOOMTest_1 {public static voidmain(String args[]){

List byteList = new ArrayList();

byteList.add(new byte[1000 * 1024 * 1024]);

}

}

看看控制台的运行效果:

使用CMD命令行指定参数运行:

java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest_1

三、线程栈

栈(JVM Stack)存放主要是栈帧( 局部变量表, 操作数栈 , 动态链接 , 方法出口信息 )的地方。注意区分栈和栈帧:栈里包含栈帧。

与线程栈相关的内存异常有两个:

a)、StackOverflowError(方法调用层次太深,内存不够新建栈帧)

b)、OutOfMemoryError(线程太多,内存不够新建线程)

1、java.lang.StackOverflowError

栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候,请求新建栈帧时,栈所剩空间小于战帧所需空间。

例如,通过递归调用方法,不停的产生栈帧,一直把栈空间堆满,直到抛出异常 :

示例代码:

packagecom.jvm;/*** 栈溢出

*@authorfeizi

* @time 2015-1-23上午9:13:11*/

public classSOFTest {public voidstackOverFlowMethod(){

stackOverFlowMethod();

}/*** 通过递归调用方法,不停的产生栈帧,一直把栈空间堆满,直到抛出异常 :

*@paramargs*/

public static voidmain(String[] args) {

SOFTest sof= newSOFTest();

sof.stackOverFlowMethod();

}

}

看看控制台运行的效果:

四、代码实现各种溢出

1、堆溢出

创建对象时如果没有可以分配的堆内存,JVM就会抛出OutOfMemoryError:java heap space异常。

堆溢出实例:

/*** VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError*/

public static voidmain(String[] args) {

List list = new ArrayList<>();int i=0;while(true){

list.add(new byte[5*1024*1024]);

System.out.println("分配次数:"+(++i));

}

}

运行结果:

分配次数:1

分配次数:2

分配次数:3

java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid2464.hprof ...

Heap dump file created [16991068 bytes in 0.047secs]

Exception in thread"main"java.lang.OutOfMemoryError: Java heap space

at com.ghs.test.OOMTest.main(OOMTest.java:16)

附:dump文件会在项目的根目录下生成

从上面的例子我们可以看出,在进行第4次内存分配时,发生了内存溢出。

2、栈溢出

栈空间不足时,需要分下面两种情况处理:

线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError

虚拟机在扩展栈深度时无法申请到足够的内存空间,将抛出OutOfMemberError

附:当前大部分的虚拟机栈都是可动态扩展的。

1、栈空间不足——StackOverflowError实例

public classStackSOFTest {int depth = 0;public voidsofMethod(){

depth++;

sofMethod();

}public static voidmain(String[] args) {

StackSOFTest test= null;try{

test= newStackSOFTest();

test.sofMethod();

}finally{

System.out.println("递归次数:"+test.depth);

}

}

}

执行结果:

递归次数:982

Exception in thread "main"java.lang.StackOverflowError

at com.ghs.test.StackSOFTest.sofMethod(StackSOFTest.java:8)

at com.ghs.test.StackSOFTest.sofMethod(StackSOFTest.java:9)

at com.ghs.test.StackSOFTest.sofMethod(StackSOFTest.java:9)

……后续堆栈信息省略

我们可以看到,sofMethod()方法递归调用了982次后,出现了StackOverflowError。

2、栈空间不足——OutOfMemberError实例

单线程情况下,不论是栈帧太大还是虚拟机栈容量太小,都会抛出StackOverflowError,导致单线程情境下模拟栈内存溢出不是很容易,不过通过不断的建立线程倒是可以产生内存溢出异常。

public classStackOOMTest {public static voidmain(String[] args) {

StackOOMTest test= newStackOOMTest();

test.oomMethod();

}public voidoomMethod(){while(true){new Thread(newRunnable() {

@Overridepublic voidrun() {

loopMethod();

}

}).start();;

}

}private voidloopMethod(){while(true){

}

}

}

运行结果:

……操作系统直接挂掉了

如果哪位大神能够成功模拟,还望指点一二。

3、持久带溢出

永久代溢出可以分为两种情况,第一种是常量池溢出,第二种是方法区溢出。

1、持久带溢出——常量池溢出

要模拟常量池溢出,可以使用String对象的intern()方法。如果常量池包含一个此String对象的字符串,就返回代表这个字符串的String对象,否则将String对象包含的字符串添加到常量池中。

public classConstantPoolOOMTest {/*** VM Args:-XX:PermSize=10m -XX:MaxPermSize=10m

*@paramargs*/

public static voidmain(String[] args) {

List list = new ArrayList<>();int i=1;try{while(true){

list.add(UUID.randomUUID().toString().intern());

i++;

}

}finally{

System.out.println("运行次数:"+i);

}

}

}

运行结果:

……比较尴尬的是,通过intern,始终无法模拟出常量池溢出,我的猜想是JDK7对常量池做了优化。

如果哪位大神成功模拟出来了,还望指点一二。

找了好久,终于弄清楚了使用string.intern()方法无法模拟常量池溢出的原因。

因为在JDK1.7中,当常量池中没有该字符串时,JDK7的intern()方法的实现不再是在常量池中创建与此String内容相同的字符串,而改为在常量池中记录Java Heap中首次出现的该字符串的引用,并返回该引用。

简单来说,就是对象实际存储在堆上面,所以,让上面的代码一直执行下去,最终会产生堆内存溢出。

下面我将堆内存设置为:-Xms5m -Xmx5m,执行上面的代码,运行结果如下:

运行次数:58162

Exception in thread "main"java.lang.OutOfMemoryError: Java heap space

at java.lang.Long.toUnsignedString(Unknown Source)

at java.lang.Long.toHexString(Unknown Source)

at java.util.UUID.digits(Unknown Source)

at java.util.UUID.toString(Unknown Source)

at com.ghs.test.ConstantPoolOOMTest.main(ConstantPoolOOMTest.java:18)

2、持久带溢出——方法区溢出

方法区存放Class的相关信息,下面借助CGLib直接操作字节码,生成大量的动态类。

public classMethodAreaOOMTest {public static voidmain(String[] args) {int i=0;try{while(true){

Enhancer enhancer= newEnhancer();

enhancer.setSuperclass(OOMObject.class);

enhancer.setUseCache(false);

enhancer.setCallback(newMethodInterceptor() {

@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throwsThrowable {returnproxy.invokeSuper(obj, args);

}

});

enhancer.create();

i++;

}

}finally{

System.out.println("运行次数:"+i);

}

}static classOOMObject{

}

}

运行结果:

运行次数:56

Exception in thread "main"

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"

虽然出现了异常,但是打印的堆栈信息似乎并不是我们想要的……

4、直接内存溢出

DirectMemory可以通过-XX:MaxDirectMemorySize指定,如果不指定,默认与Java堆的最大值(-Xmx指定)一样。

NIO会使用到直接内存,你可以通过NIO来模拟,在下面的例子中,跳过NIO,直接使用UnSafe来分配直接内存。

public classDirectMemoryOOMTest {/*** VM Args:-Xms20m -Xmx20m -XX:MaxDirectMemorySize=10m

*@paramargs*/

public static voidmain(String[] args) {int i=0;try{

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

field.setAccessible(true);

Unsafe unsafe= (Unsafe) field.get(null);while(true){

unsafe.allocateMemory(1024*1024);

i++;

}

}catch(Exception e) {

e.printStackTrace();

}finally{

System.out.println("分配次数:"+i);

}

}

}

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError

at sun.misc.Unsafe.allocateMemory(Native Method)

at com.ghs.test.DirectMemoryOOMTest.main(DirectMemoryOOMTest.java:20)

分配次数:27953

总结:

栈内存溢出:程序所要求的栈深度过大。

堆内存溢出: 分清内存泄露还是 内存容量不足。泄露则看对象如何被 GC Root 引用,不足则通过调大-Xms,-Xmx参数。

永久代溢出:Class对象未被释放,Class对象占用信息过多,有过多的Class对象。

直接内存溢出:系统哪些地方会使用直接内存。

java溢出 事件触发_Java各种溢出相关推荐

  1. java栈堆溢出怎么解决_java内存溢出示例(堆溢出、栈溢出)

    堆溢出:/** * @author LXA * 堆溢出 */ public class Heap { public static void main(String[] args) { ArrayLis ...

  2. java委托事件模型_JAVA授权事件模型讲解(原创)

    JAVA的授权事件模型包含三个概念:事件源,事件,事件监听器. 一,事件源:一个产生事件的对象.当这个对象的内部状态改变时,事件就会产生.一个事件源必须注册一个事件监听器已使监听器能够可以接受一个特定 ...

  3. java 溢出与泄露_Java内存溢出与栈溢出

    一.背景知识 1.JVM体系结构 2.JVM运行时数据区 3.JVM内存模型 JVM运行时内存 = 共享内存区 + 线程内存区 3-1.共享内存区 共享内存区 = 持久带 + 堆 持久带 = 方法区 ...

  4. java溢出怎么处理_java数据溢出怎么处理?

    publicstaticvoidmain(String[]args){//TODO自动生成的方法存根Scannersc=newScanner(System.in);Stringaa=sc.nextLi ...

  5. java自定义事件案例_Java Custom Exception Example(Java自定义异常案例)

    In this example we will look briefly(短暂的) at the basics of Exception, in Java Programming Language. ...

  6. java添加事件监听器_Java事件监听器的四种实现方式

    自身类作为事件监听器 外部类作为事件监听器 匿名内部类作为事件监听器 内部类作为事件监听器 自身类作为事件监听器: 1 import javax.swing.*;2 import java.awt.* ...

  7. java swing事件机制_java SWing事件调用的两种机制

    /** * java swing中事件调用的两种机制: * (一)响应机制 * (二)回调机制 */ package test; import java.awt.*; import java.awt. ...

  8. java aio事件模型_JAVA AIO

    JDK7中新增了一些与文件(网络)I/O相关的一些API,这些API被称为NIO2,或称为AIO(Asynchronous I/O). 全部章节传送门: AIO 介绍 AIO最大的一个特性就是异步能力 ...

  9. Java面向事件编程_Java面向事件编程

    基于Java的基础课程给教师和学生都提出了新的挑战.虽然Java要比C++简单,但Java作为一种具有大量标准库的面向对象语言这一事实既带来了新的复杂性,同时也带来了新的机会.. 本书作为计算机科学基 ...

  10. java crud事件回调_java回调机制 - 神是到着念的个人空间 - OSCHINA - 中文开源技术交流社区...

    软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用.回调和异步调用.同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用:回调是一种双向调用模式,也就是 ...

最新文章

  1. swift_030(Swift 的访问控制)
  2. mysql数据库备份工具expdb,使用expdp完成自动备份数据库案例以及遇到的问题
  3. 政府数据集中异地备份概述
  4. 前端学习(2702):重读vue电商网站23之element-ui 如何按需导入
  5. 确认! Python夺冠,Java“被迫”退出竞争舞台,网友:崩溃!
  6. UFLDL 教程学习笔记(二)反向传导算法
  7. Caffe学习:pycaffe利用caffemodel进行分类(2)
  8. 风雨秋招路-CV太难了-记得复盘
  9. 计算机安装pdf打印机驱动,虚拟打印驱动PDF打印机
  10. hget如何获取多个value_Redis(五):hash/hset/hget 命令源码解析
  11. 手机上网页是html,如何在手机上制作自己的网页
  12. Grub4dos安装和启动完全指南
  13. 深度强化学习-策略梯度算法深入理解
  14. Tackling Climate Change with Machine Learning
  15. 深入浅出java并发编程(线程池)
  16. Linux之禅道安装
  17. ksu7对讲机调频软件_万能对讲机写频软件
  18. android 接口sign,[原创]小米APP登录接口env、envkey、sign、hash算法
  19. 解决【无法对套接字执行操作,因为系统缺少足够的缓冲区空间,或者因为系统上的队列已满127.0.0.1:8086】问题
  20. 朋友,别再干巴巴地“陈述”简历了,10条写作的技巧分享给你

热门文章

  1. 联通将推自有品牌手机沃Phone 基于Android
  2. 新卡插手机显示无服务器,手机插卡无服务怎么回事
  3. 每日思考第 61 期:职场PUA与情场PUA
  4. RoaringBitmap位图数据结构及源码分析
  5. 海康威视监控摄像头连接电脑教程
  6. C笔记《C Primer Plus 6E》
  7. Pandas学习——分组
  8. Raspberry-Pi-PICO系列--第八篇 高级篇使用SWD接口下载和调试(第一章)
  9. python文件双击闪退_解决python文件双击运行秒退的问题
  10. macOS Monterey 12.4 (21F79) 正式版 ISO、IPSW、PKG 下载