Java虚拟机(Java Virtual Machine,JVM)定义了多种运行时数据区,这些数据区用于程序的执行期间。一些数据区在Java虚拟机启动时创建,在Java虚拟机退出时销毁,而其它的数据区则是针对每个线程的。针对每个线程的数据区在创建线程时创建并在该线程退出时销毁。
- Java进程堆(Java Process Heap)
Java运行为一个单一的进程,并且不与别的进程共享内存,每一个进程都会分配内存空间,我们可以将其称之为进程堆(Process Heap)。在大多数情况下由JVM来管理进程堆,但是对于JNI管理的内存空间却是一个例外。 Java虚拟机的进程堆的最大空间是受限的,在32位系统下大致为2GB。
Java虚拟机创建了一个Java堆(Java Heap),它是进程堆(Process Heap)的一部分。在Java进程堆中除了Java堆区,还有一个非堆区,其中主要包括线程栈(Thread Stack)、方法区(Method Area) 、垃圾回收(Garbage Collection)和JNI分配的内存空间等 。
- Java堆( Java Heap )
Java堆在所有的Java虚拟机线程间共享,所有的实例和数组均从该运行时数据区分配空间。该堆在虚拟机启动时创建,对象的堆存储空间被自动存储管理系统(也就是垃圾回收器,Garbage Collector)回收,对象从不被显式地释放。该堆可以是固定大小,也可以根据计算的需要增长,或者在不需要更大的堆空间时进行压缩。堆所据有的内存空间不一定是连续的。
一个Java虚拟机实现可能会提供给程序员或者用户控制Java堆初始大小的能力。同样,如果Java堆是动态扩展或者压缩的,程序员或者用户也可能具有控制堆大小最大值和最小值的能力。
在启动Java虚拟机时可以使用如下选项来配置Java堆:
- -Xms<size>:设置Java堆大小的初始值;
- -Xmx<size>:设置Java堆大小的最大值。
如果计算需要的堆大小超出自动存储管理系统所能提供的可用堆空间的大小,那么Java虚拟机将会抛出OutOfMemoryError(OOM),即java.lang.OutOfMemoryError: Java heap space。
- 线程栈(Java Thread Stack)
每一个Java虚拟机线程均有一个私有的Java虚拟机栈,它在线程创建的同时创建。一个Java虚拟机栈用来存储帧(Frame)。
注:帧(Frame)用来存储数据和中间结果,也用来实现动态链接、方法的返回值和派发异常等。一个新的帧在方法调用时被创建,并在方法调用完成时销毁,不管方法是正常结束还是异常结束。帧从创建该帧的线程的Java虚拟机栈中分配空间。每一个帧拥有它自己的局部变量数组、操作数栈和一个对当前方法所在类的运行时常量池的引用。
线程栈中保存局部变量和中间结果,并在方法调用和返回的过程中发挥作用。因为除了压入和弹出帧,线程栈从不被直接操作,帧可以是从堆分配的。一个Java虚拟机栈的内存空间不需要是连续的。
Java虚拟机规范允许Java虚拟机栈可以是固定大小的或者是根据计算的需要动态扩展和压缩的。如果Java虚拟机栈是固定大的,Java虚拟机栈的大小可以在栈创建时独立地选择。一个Java虚拟机实现可能会提供给程序员或者用户控制Java虚拟机栈初始大小的能力。同样,如果在该栈可以动态扩展或者压缩的情况下,那么也可能提供给程序员或者用户控制该栈大小的最大值和最小值的能力。使用-Xss<size>选项便可以指定Java线程栈的大小。
下述异常情况可能与Java虚拟机栈相关联:
- 如果线程中的计算需要的Java虚拟机栈超出所允许的大小,那么Java虚拟机便会抛出StackOverflowError;
- 如果Java虚拟机栈可以动态扩展,并且尝试了扩展,但是没有足够的内存空间来实现扩展,或者没有足够的内存空间来为一个新线程创建一个初始的Java虚拟机栈,Java虚拟机便会抛出OutOfMemeoryError。
- 方法区(Method Area)
方法区又可以称为永久空间(Permanent Space)。Java虚拟机均有一个方法区,该区在所有的Java虚拟机线程间共享。方法区与传统编程语言编译后代码的存储区或者Unix进程中的“text”段相似。它存储每一个类的结构,例如运行时常量池、字段和方法的数据、方法和构造函数的代码,包括用在类中和实例初始化和接口类型初始化的特别的方法等。
方法区在虚拟机启动时创建,虽然方法区逻辑上是堆的一部分,简单的实现可能选择不进行垃圾回收或者压缩它。
一个Java虚拟机实现可能会提供该程序员或者用户控制方法去初始大小的能力,同样,在方法区可以改变大小的情况下,提供给用户控制方法区最大值和最小值的能力。
通过以下选项可以完成方法区大小的设置:
- -XX:PermSize=<size>:可以设置方法区的初始值
- -XX:MaxPermSize=<size>:可以设置方法区的最大值。
下述异常情况可能与方法区相关:
如果方法区不能满足分配请求的需要,那么Java虚拟机可能会抛出OutOfMemeorError的异常,即java.lang.OutOfMemoryError: PermGen space 。
以上内容主要来源于JVM规范文档和Filip Hanik的Inside The Java Virtual Machine,详细说明请参见原文。