Java虚拟机结构原理以及内存溢出详细介绍



Java虚拟机结构原理,内存分配以及内存溢出java.lang.OutOfMemoryError: PermGen space介绍,Java虚拟机(Java Virtual Machine,JVM)定义了多种运行时数据内存区,内存数据区用在程序的执行期间。数据区在Java虚拟机启动时创建,在Java虚拟机关闭的时候销毁,而别的的数据区就是针对每个线程的。对于每个java线程的数据区在创建java线程时创建而且在该线程退出时销毁。

  • Java进程堆(Java Process Heap)

Java运行时是一个单一的进程,而且不与别的进程共享内存,每个java进程都会分配内存空间,可以把其称之为进程堆(Process Heap)。在大多数情况下由java虚拟机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堆:

  1. -Xms<size>:设置Java堆内存大小的初始值;
  2. -Xmx<size>:设置Java堆内存大小的最大值。

假如计算需要的堆大小超出自动存储管理系统所能提供的可用堆空间的大小,那么Java虚拟机就会抛出OutOfMemoryError(OOM),即java.lang.OutOfMemoryError: Java heap space。

  • java线程栈(Java Thread Stack)


每个Java虚拟机线程都有一个私有的Java虚拟机栈,它在线程创建的同时创建。一个Java虚拟机栈用来存储帧(Frame)。

注:帧(Frame)用来存储数据和中间结果,也用来实现动态链接、方法的返回值和派发异常等。一个新的帧在方法调用时被创建,并在方法调用完成时销毁,不管方法是正常结束还是异常结束。帧从创建该帧的线程的Java虚拟机栈中分配空间。每一个帧拥有它自己的局部变量数组、操作数栈和一个对当前方法所在类的运行时常量池的引用。
线程栈中保存局部变量和中间结果,并在方法调用和返回的过程中发挥作用。因为除了压入和弹出帧,线程栈从不被直接操作,帧可以是从堆分配的。一个Java虚拟机栈的内存空间不需要是连续的。
Java虚拟机规范允许Java虚拟机栈可以是固定大小的或者是根据计算的需要动态扩展和压缩的。如果Java虚拟机栈是固定大的,Java虚拟机栈的大小可以在栈创建时独立地选择。一个Java虚拟机实现可能会提供给程序员或者用户控制Java虚拟机栈初始大小的能力。同样,如果在该栈可以动态扩展或者压缩的情况下,那么也可能提供给程序员或者用户控制该栈大小的最大值和最小值的能力。使用-Xss<size>选项便可以指定Java线程栈的大小。
下述异常情况可能与Java虚拟机栈相关联:

  1. 如果线程中的计算需要的Java虚拟机栈超出所允许的大小,那么Java虚拟机便会抛出StackOverflowError;
  2. 如果Java虚拟机栈可以动态扩展,并且尝试了扩展,但是没有足够的内存空间来实现扩展,或者没有足够的内存空间来为一个新线程创建一个初始的Java虚拟机栈,Java虚拟机便会抛出OutOfMemeoryError。
  • java方法区(Method Area)

java方法区又能够称为永久空间(Permanent Space)。Java虚拟机都有一个方法区,java方法区(Method Area)在所有的Java虚拟机线程间共享。方法区以及传统编程语言编译后代码的存储区或者Unix进程中的“text”段相似。它存储每一个类的结构,例如运行时常量池、字段和方法的数据、方法和构造函数的代码,包括用在类中和实例初始化和接口类型初始化的特别的方法等。
方法区在虚拟机启动时创建,虽然方法区逻辑上是堆的一部分,简单的实现可能选择不进行垃圾回收或者压缩它。
一个Java虚拟机实现可能会提供该程序员或者用户控制方法去初始大小的能力,同样,在方法区可以改变大小的情况下,提供给用户控制方法区最大值和最小值的能力。

通过以下选项可以完成方法区大小的设置:

  1. -XX:PermSize=<size>:可以设置方法区的初始值
  2. -XX:MaxPermSize=<size>:可以设置方法区的最大值。

下面的异常情况很可能与方法区相关:
假如方法区不能满足分配请求的需要,那Java虚拟机有可能会抛出OutOfMemeorError的异常,即java.lang.OutOfMemoryError: PermGen space 。