上一篇:Java虚拟机(零):JVM概述
一、JVM内存管理概述
- 为什么Java虚拟机有内存管理机制?
Java语言与C++相比,有一个很大的特点:不用手动管理内存。这样减少了对内存的人为操作,尽可能的避免了由于指针所导致的内存泄露问题。但如果不手动管理内存,那内存该由谁来管理呢?此时,Java虚拟机便担当起了这个重任。为了更好的去管理内存,JVM便设计了一套自己的内存管理机制。
二、JVM内存结构的组成
一般来说,我们所说的Java虚拟机的内存结构是指Java虚拟机运行时的数据区中的结构。在JVM运行时的数据区中,一共包含五个部分:堆、方法区(非堆)、程序计数器、虚拟机栈、本地方法栈。另外,运行时还需要执行引擎、本地库接口(连接本地方法库)。以下是JVM运行时的数据区结构图:
三、JVM内存结构的详述
1、堆(Heap)
作用:主要用于存放创建的对象实例(几乎所有的对象实例,包括数组,都存放在堆中)。它也是垃圾回收器管理的主要区域,又被称为“GC堆”。
特点:1、线程共享;
2、堆是虚拟机内存中最大的一块内存空间;
3、Java堆可以在物理上不连续而逻辑上必须连续;
4、当堆中没有可分配的空间时,会抛出OutOfMemoryError异常;
5、为了更快的分配/回收内存,堆中可能会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB );
6、在虚拟机启动的时候创建。
2、方法区
作用:主要存储类信息、常量、静态变量、即时编译器编译的代码等数据。
特点:1、线程共享;
2、此区域使用永久代来实现;
3、当方法区没有可分配的空间时,会抛出OutOfMemoryError异常。
3、程序计数器
作用:存储当前线程所执行方法的字节码行号,相当于一个字节码行号指示器。
特点:1、程序计数器是虚拟机中一块较小的内存;
2、线程私有;
3、虚拟机中唯一一块不存在OutOfMemoryError的区域;
4、执行Java方法时记录当前字节码行号,执行Native方法时,程序计数器为空。
4、虚拟机栈
作用:执行Java(字节码)方法的区域,主要用于栈帧的出栈和入栈。
特点:1、线程私有;
2、生命周期与线程相同;
3、如果线程请求的栈的深度大于了虚拟机栈所允许的深度,则会抛出StackOverflowError;
4、如果虚拟机栈的内存空间被全部使用且没有可用的扩展空间时,则会抛出OutOfMemoryError异常。
5、本地方法栈
作用:执行虚拟机Native方法的区域。
特点:和虚拟机栈很类似,Java虚拟机规范中没有对本地方法栈的语言、使用方式、数据结构有强制的规定。有时本地方法栈和虚拟机栈可以作为同一个(Sun HotSpot就将本地方法栈和虚拟机栈合二为一了)。
6、其他
栈帧:每个方法执行的同时都会创建一个栈帧,栈帧存储方法的局部变量表、操作数栈、方法出入口信息、动态链接等信息。在代码编译时期,JVM已经确定了栈帧局部变量表的大小以及栈的深度。栈中局部变量表存放着编译期可知的基本类型、returnAddress类型(指向了一条字节码指令的地址)和对象引用(可能是指向对象的指针或者也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。其中,long和double占两个局部变量空间,其余数据类型只占用一个。由局部变量表的存储内容推断,从方法开始运行到结束的过程中,此方法在栈帧中所分配的局部变量表的大小是完全确定的。
运行时常量池:运行时常量池位于方法区中,用于存储编译期和运行期生成的各种常量、符号引用和直接引用。如果常量池不足以为常量或引用提供足够的内存,则会抛出OutOfMemoryError异常。
直接内存:直接内存不属于虚拟机运行时数据区。由于JDK1.4新加入了NIO(New Input/Output)的类,使用了基于通道(Channel)的与缓冲区的方式,为了提高性能,虚拟机使用堆中的DirectByteBuffer对象对直接内存进行操作。这样避免了虚拟机在Native堆和Java堆中来回复制。
下一篇:Java虚拟机(二):类文件详解
注:本文是作者参考《深入理解Java虚拟机》和其他文档写作而成,若有侵权,请联系作者。
由于作者水平有限,写作之中若有纰漏,还望指出。欢迎交流