您的位置:首页 > 编程语言 > Java开发

# 2 java内存区域与内存溢出异常

2016-06-14 14:18 465 查看
2.2运行时数据区域

Java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁时间,有的区域随着虚拟机进程的启动而存在,有些则依赖用户线程的启动和结束而建立和销毁。

1)程序计数器(线程私有内存)

一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变计数器的值来选取下一条需要执行的直接字节码指令。

2)Java虚拟机栈(线程私有内存)

描述java方法的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程

此区域有两种异常状况:

1 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowErro异常;

2如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryErro异常。

3)本地方法栈(线程私有内存)

本地方法栈与虚拟机栈发挥的作用非常相似,它们之间的区别不过是虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机用到的Native方法服务。抛出的异常与java虚拟栈一样。

4)Java堆

对于大多数应用来说,java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域唯一存在的目的就是存放对象实例,几乎所有对象实例都在这里分配内存。

Java堆是垃圾收集器管理的主要区域,很多时候也被称为“GC堆”(Garbage Collection Heap)如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,会抛出OutOfMemoryError异常。

5)方法区

方法区与java堆区一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常。

6)运行时常量池

运行时常量池是方法区的一部分。Class文件中除了有类的版本,地段,方法,接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用这部分内容将在类加载后进入方法区的运行时常量池中存放

7)直接内存

直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用而且也可能导致OutOfMemoryErro异常出现。

在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道与缓存区的IO方式,它可以使用Native函数直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在java 堆和native堆中来回复制数据。

java虚拟机运行时数据区



2.3对象的创建

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载,解析和初始化过。如果没有,那必须先执行相应的类加载过程。

在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从java堆中划分出来。

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息。这些信息存放在对象的对象头中。

以上工作完成后从虚拟机的视角来看,一个新的对象已经产生了,但从java程序的视角来看,对象创建才刚刚开始-方法还没执行,所有字段都还为零。所以一般来说(由字节码中是否跟随invokespace指令所决定),执行new指令之后会接着执行方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

实战:OutOfMemoryError异常

在java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError异常的可能。

Java堆溢出

Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。

代码清单中代码限制java堆的大小为20MB,不可扩展(将堆得最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过参数-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析。

import java.util.ArrayList;
import java.util.List;

/**
* VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
* @author ykw
*
* Jun 3, 2016
*/
public class HeapOOM {
static class OOMobject{

}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<OOMobject>list=new ArrayList<OOMobject>();
while(true){
list.add(new OOMobject());

}
}

}


以上代码 在eclipse中运行感觉有没有设置参数都没有对虚拟机占用的内存产生影响

而且短时间的运行并没有让虚拟机栈和本地方法栈溢出

由于在Hotspot虚拟机中并不区分虚拟栈和本地方法栈,因此,对于Hotspot来说,虽然-Xboss存在,但实际上是无效的,栈容量只由-Xss参数设定。关于虚拟机栈和本地方法栈,在java虚拟机规范中描述了两种异常:

1 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverFlowErro异常。

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

运行结果

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  虚拟机 java 内存