您的位置:首页 > 其它

JVM学习笔记-内存处理

2013-06-22 17:17 337 查看
大多数JVM将内存区域划分为Method Area(Non-Heap),Heap,Program Counter Register,Java Method Statck,Native Method Stack和Direct Memomry(备注:Directory Memory并不属于JVM管理的内存区域)。前三者一般翻译为:方法区、堆、程序计数器。但不同的资料和书籍对于后者的翻译名不尽相同,这里将他们分别翻译为:Java方法栈、本地方法栈和直接内存区域。对于不同的JVM。内存区域划分可能会有所差异,比如Hot
Spot就将Java方法栈和本地方法栈合二为一,统称为方法栈(Method Stack)

首先我们熟悉一下一个一般的Java程序的工作过程。一个Java源文件,会被编译成字节码(ByteCode),然后告知JVM程序的运行入口,再被JVM通过字节码解释器加载运行,具体请参考JVM学习笔记-基础知识。那么程序开始运行后,是如何涉及到各内存区域的呢?

概括地说,JVM每遇到一个线程,就为其分配一个程序计数器、Java方法栈和本地方法栈。当线程终止时,两者所占有的内存空间会被释放掉。栈中保存的是栈帧,可以说每个栈帧对应一个“运行现场”。如果出现一个局部对象,则它的实例数据被保存在堆中,而类数据被保存在方法区。

我们用上面这一段文字就描述完了每个内存区域的基本功能,但是这还是比较粗糙,下面就分别介绍它们的存储对象、生存周期与空间管理策略。

程序计数器

线程特性:私有
存储内容:字节码文件指令地址(Java Methods),或Undefined(Native Methods)
生命周期:随线程而生死
空间策略:占用内存很小

这个最简单,就先从它说起。程序计数器,是线程私有(与线程共享相对)的,也就是说有N个线程,JVM就会分配N个程序计数器。如果当线程在执行一个Java方法,程序计数器记录着线程所执行的字节码文件中的指令地址。线程执行的是一个Native方法,则计数器值为Undefined。

程序计数器的生存周期多长呢?显然程序计数器是伴随着线程而生,伴随线程死而死的,并且程序计数器占用的内存空间也很小。

Java方法发栈与本地方法栈

Java方法栈也是线程私有的,每个Java方法栈都是由一个个栈帧组成的,每个栈帧是一个方法运行期的基础数据结构,它存储局部变量表,操作数栈、动态链表、方法出口等信息。当线程调用了一个Java方法时,一个栈帧就被压入(Push)到相应的Java方法栈。当线程从一个Java方法返回时,相应的Java方法栈就弹出(Pop)一个栈帧。

其中要详细介绍的是局部变量表,它保存着各种基本数据类型和对象引用(Object reference)。基本数据类型包括boolean、byte、char、short、int、long、float、double。对象引用,本质就是一个地址(也可以说是一个“指针”),该地址是堆中的一个地址,通过这个地址可以找到相应的Object(注意“找到”,具体原因会在下面解释)。而这个地址找到相应Object的方式有两种。一种是该地址存储着Pointer to Object Instance Data和Pointer
to Object Class Data;另一种是该地址存储着Object Instance Data,其中又包含有Pointer to Object Class Data。



图1:间接方式



图2:直接方式

第一种方式,Java方法栈中有Handler Pool和Instance Pool,无论哪种方式,Object Class Data都是存储在方法区的,Object Instance Data都是存储在堆中的。

本地方法栈和Java方法栈相类似,这里不再赘述。



堆是在启动虚拟机的时候划分出来的区域,其大小由参数或者默认参数指定。当虚拟机终止运行时,会释放堆内存 。一个JVM只有一个堆,它自然是线程共享的。堆中存储的是所有的Object Instant Data以及数组(不过随着栈上分配技术、标量替换技术等优化手段的发展,对象也不一定都存储在堆上了),这些Instance由垃圾管理器(Grabage Collector)管理,具体会在后面的章节阐述。

堆可以是由不连续的物理内存空间组成的,并且既可以固定大小,也可以设置为可扩展的(Scalable)。

方法区

通过上述Java方法栈的介绍,大家已经知道Object Class Data是存储在方法区的。除此之外,常量、静态变量、JIT编译后的代码也都都是在方法区。正因为方法去存储的数据与堆有一种类比关系,所以还被称为Non-Heap。方法区也可以是内存不连续的区域组成的,并且可设置为固定大小,也可以设置称为可扩展的,这点与堆一样。

方法区内部有一个非常重要的区域,叫做运行时常量池(Runtime Constant Pool,简称RCP)。在字节码文件中常量池(Constant Pool Table),用于存储编译器产生的字面量和符号引用。每个字节码文件中的常量池在类被加载后,都会存储到方法区中。值得注意的是,运行时产生的新常量也可以被放入常量池中,比如String类中的intern()方法产生的常量。

直接内存区

直接内存区并不是JVM管理的内存区域的一部分,而是其之外。该区域也会在Java开发中使用到,并且存在导致内存溢出的隐患。如果对NIO有所了解,应该会知道NIO是可以使用Native Methods来使用直接内存区的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: