1,运行时数据区域

根据JAVA虚拟机规范的规定:JAVA虚拟机所管理的内存将会包括以下几个运行时数据区域

程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器,通过改变计数器的值来选取下一条需要执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等基础功能。每条线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存,也是唯一不会出现OutOfMemoryError情况的区域。

JAVA虚拟机栈(Java Virtual Machine Stacks)也是线程私有,它的生命周期与线程相同,用来描述JAVA方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等。每一个方法从被调用到执行完成的过程,也就一个栈帧在虚拟机栈从入栈到出栈的过程。在JAVA虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。如果虚拟机可以动态扩展,当扩展到无法申请到足够的内存时,会抛出OutOfMemoryError异常。

本地方法栈(Native Method Stacks)与上述的虚拟机栈非常类似,只是虚拟机栈为执行JAVA方法服务,而本地方法栈为虚拟机使用到的Native方法服务。

JAVA堆(Java Heap)是Java虚拟机所管理内存中最大的一块,被所有线程共享,在虚拟机启动时创建,此内存区域的唯一目的就是为了存放对象实例。JAVA堆是垃圾回收器管理的主要区域。如果堆中没有足够的内存完成实例分配,并且堆无法扩展时,将会抛出OutOfMemoryError异常。

方法区(Method Area)方法区也被称为“持久代”,此内存区域与堆一样,也是线程共享的。它用于存储已被虚拟机加载的类(java.lang.Class)信息、常量、静态变量、即时编译器编译后的代码等数据。垃圾回收行为在这个区域是比较少见的,并且可以选择不回收。

当此区域无法满足内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池(Runtime Constant Pool)是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池。当常量池无法再分配到内存时,也会抛出OutOfMemoryError异常。

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是JAVA虚拟机规范中定义的内存区域,但是这部分内存也频繁被使用,并且也可能抛出OutOfMemoryError异常。如NIO可以使用Native函数库直接分配堆外内存。

例子:

Object obj = new Object();

  假设这段代码出现在方法体中,那"Object obj"这部分的语义将会反映到JAVA栈的局部变量表中,作为一个reference类型的数据出现,因此就存在虚拟机栈中。 而"new Object();"这部分的语义将会反映到JAVA中,形成一个存储了Object类型所有实例数据值(Instance Data,对象中各个实例字段的数据)的结构化内存。另外,在JAVA堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中。

句柄:JAVA堆中会划分出一块内存来作为字柄池,reference存放的是对象的句柄地址,而句柄中包含了对象实例和类型数据各自的具体地址信息: 如下图所示 :

2,实战:OutOfMemoryError异常

在JAVA虚拟机规范描述中:除了程序计数器外,其它几个内存区域都有发生OutOfMemoryError异常的可能,本节通过若干实例来验证异常发生的场景。

注意:每个示例代码的开头都会注明虚拟机启动参数的设置,具体设置方法如下图:

JAVA堆溢出(-Xms表示堆内存的初始值,-Xmx表示堆内存的最大值。)

//-Xms20m -Xmx20m
public class HeapOOm {
static class OOmObject{
}
public static void main(String[] args) {
List<OOmObject> oomList = new ArrayList<OOmObject>();
while(true){
oomList.add(new OOmObject());//不断生成新对象
}
}
}

上例通过不断生成新对象,导致内存溢出。运行结果如下

虚拟机栈和本地方法栈溢出

写个递归程序,如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

一般在单线程程序情况下无法产生OutOfMemoryError异常

//-Xss128k
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak(){
stackLength ++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Exception e) {
System.out.println("stack lenght:"+oom.stackLength);
throw e;
}
}
}

下面这个示例,尝试使用多线程方式得到OutOfMemeoryError的结果,因为栈是线程私有的,线程多也会方法区溢出。

//-Xss2M
public class JavaVMStackOOM {
private void dontStop(){
while (true) {
}
}
public void stackLeakByThread(){
int i = 0;
while(true){
System.out.println(i++);
Thread thread = new Thread(new Runnable() {
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}

运行时常量池溢出

//-XX:PermSize=10M -XX:MaxPermSize=10M
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int i = 0;
while(true){
System.out.println(i);
list.add(String.valueOf(i++).intern());
}
}
}

运行结果

String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;

 用new String("xxxx") 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

关于JAVA字符串常量池可以看下面这个介绍,很详细

http://blog.csdn.net/longtenggdf/article/details/4606225

方法区溢出:

方法区用于存放Class的相关信息,如类名,访问修饰符,常量池,字段描述,方法描述等。对于这个区域的测试,大概思路是运行时产生大量的类去填满方法区,直到溢出,本例使用CGLib直接操作字节码,生成大量动态类

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//-XX:PermSize=10M -XX:MaxPermSize=10M
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();
}
}
static class OOMObject{
}
}

本机直接内存溢出

DirectMemory容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与JAVA堆的最大值一样

import java.lang.reflect.Field;
import sun.misc.*;
//-Xmx20M -XX:MaxDirectMemorySize=10M
public class DirectMemoryOOM {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
}
}

JVM基础知识1--JAVA内存区域与内存溢出异常的更多相关文章

  1. JVM基础知识(1)-JVM内存区域与内存溢出

    JVM基础知识(1)-JVM内存区域与内存溢出 0. 目录 什么是JVM 运行时数据区域 HotSpot虚拟机对象探秘 OutOfMemoryError异常 1. 什么是JVM 1.1. 什么是JVM ...

  2. JVM 基础知识

    JVM 基础知识(GC) 2013-12-10 00:16 3190人阅读 评论(1) 收藏 举报 分类: Java(49) 目录(?)[+] 几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看 ...

  3. 深入理解JVM之JVM内存区域与内存分配

    深入理解JVM之JVM内存区域与内存分配 在学习jvm的内存分配的时候,看到的这篇博客,该博客对jvm的内存分配总结的很好,同时也利用jvm的内存模型解释了java程序中有关参数传递的问题. 博客出处 ...

  4. 【转】JVM 基础知识

    几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看,又添加了一些东西.突然发现,基础真的很重要.学习的过程是一个由表及里,再由里及表的过程,所谓的“温故而知新”.而真正能走完这个轮回的人,也就能 ...

  5. 深入理解java虚拟机系列(一):java内存区域与内存溢出异常

    文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...

  6. JVM基础知识GC

    在网上看到一篇很不错的讲解JVM GC的文章,看完之后觉得可以留着以后多看几遍便转载了下来.但是找了半天也没有找到原作者地址.抱歉不能标明原文地址了.以下是文章内容. 几年前写过一篇关于JVM调优的文 ...

  7. 深入理解java虚拟机-第二章:java内存区域与内存泄露异常

    2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...

  8. JVM 基础知识(GC)

    几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看,又添加了一些东西.突然发现,基础真的很重要.学习的过程是一个由表及里,再由里及表的过程,所谓的"温故而知新".而真正能走完 ...

  9. 第二章Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 一.概述 对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问 题, ...

  10. JVM内存区域与内存溢出异常

    Java虚拟机在执行java程序时会把它所管理的内存会分为若干个不同的数据区域,不同的区域在内存不足时会抛出不同的异常. >>运行时数据区域的划分 (1)程序计数器程序计数器(Progra ...

随机推荐

  1. 照片Urls

    http://img.my.csdn.net/uploads/201402/16/1392530364_7835.jpg http://img.my.csdn.net/uploads/201402/1 ...

  2. 使用svnadmin对VisualSVN进行项目迁移

    使用svnadmin对VisualSVN进行项目迁移 导出1> 启动命令行cmd2> 运行%VISUALSVN_SERVER%\bin\svnadmin dump PATH-TO-REPO ...

  3. java web项目创建

    https://www.cnblogs.com/kangjianwei101/p/5621738.html

  4. Vue之vue.js声明式渲染

    Html: <div id="app"> {{ message }} </div> Vue: var app = new Vue({ el: '#app', ...

  5. DOM 中 Property 和 Attribute 的区别(转)

    property 和 attribute非常容易混淆,两个单词的中文翻译也都非常相近(property:属性,attribute:特性),但实际上,二者是不同的东西,属于不同的范畴. property ...

  6. assert.equal()

    assert.equal(actual, expected[, message]) 使用相等运算符(==)测试 actual 参数与 expected 参数是否相等(通俗解释equal方法接受三个参数 ...

  7. Flask12 数据库连接、创建数据库用户、注册模块

    1 连接数据库 1.1 安装 flask-sqlalchemy flask-sqlalchemy 会让 sqlalchemy 使用起来更加方便 pip3 install flask-sqlalchem ...

  8. p2023&bzoj1798 维护序列

    传送门(洛谷) 传送门(bzoj) 题目 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全 ...

  9. 9. CTF综合靶机渗透(二)

    靶机说明 Welcome to the world of Acid. Fairy tails uses secret keys to open the magical doors. 欢迎来到酸的世界. ...

  10. Sharepoint2013商务智能学习笔记之Excel Service服务配置(四)

    第一步,新建Excel Service应用程序 第二步,管理中心,在应用程序管理区域点管理服务应用程序,进入应用程序管理列表, 再点击刚才新建好的ExcelServiceApplication进入ex ...