首页 > 技术文章 > Java虚拟机各区域介绍

joeytt 2015-09-22 16:57 原文

Java虚拟机在执行Java程序时会把它所管理的内存划分为若干个不同的数据区域。
Java虚拟机运行时区域图

程序计数器

程序计数器( Program Counter Register ) 是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
由于Java 虚拟机的多线程是通过线程轮流切换井分配处理器执行时间的方式来实现的,在任何一个确定的时刻,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器直不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
如果线程正在执行的是一个 Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址:如果正在执行的是 Native方法,这个计数器值则为空(undefined)。

虚拟机栈

Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。每个方法被执行的时候都会同时创建一个栈帧(Stack Frame 用于存储局部变量表、操作栈、动态链接、方法出口等信息。

本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码〉服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

Java堆

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 Java堆是垃圾收集器管理的主要区域 因此很多时候也被称做“GC堆”(Garbage Collected Heap)。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以 Java 堆中还可以细分为:新生代和老年代:再细致 点的有 Eden 空间、 From Survivor 空间、 To Survivor 空间等。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。

方法区

方法区(Method Area)与Java堆一样,是各个钱程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap (非堆〉,目的应该是与 Java 堆区分开来。对于习惯在 HotSpot 虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区称为“永久代”( Permanent Generation ),本质上两者并不等价,仅仅是因为 HotSpot虚拟机的设计团队选择把 GC 分代收集扩展至方能区,或者说使用永久代来实现方能区而已,其他虚拟机〈如 EA JRockit IBM J9 等〉来说是不存在永久代的概念的。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。

运行时常量池

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

直接内存

直接内存( Direct Memory )井不是虚拟机运行时数据区的一部分,也不是 Java虚拟机规范中定义的内存区域。JDK 1.4中新加入NI(New Input/Output)类,引入了一种基于通道(Channe与缓冲区Buffer)的方式,它可以使用 Native 函数库直接分配堆外内存,后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
显然,本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括RAM及SWAP区或者分页文件〉的大小及处理器寻址空间的限制。

推荐阅读