JVM之内存区域划分

2018-07-10  本文已影响0人  Lebens

JVM在执行Java过程中会把它所管理的内存划分为一下几个运行时数据区域:

JVM运行时内存区域.png

程序计数器

程序计数器是一块内存较小的空间,可以看作是当前线程执行的字节码的行号指示器,是每个线程私有的内存区域。

Java虚拟机栈

Java虚拟机栈也是线程私有的,生命周期与线程相同。

Java虚拟机栈描述的是Java方法执行的内存模型:每一个方法创建时会创建一个栈帧,每一个方法从开始调用到执行完成过程,对应着一个栈帧在虚拟机栈中的入栈和出栈的过程。

上面提到的栈帧是方法运行时的基础数据结构,用于存储

等信息。(图片来源于网络)

栈帧.jpg

通常人们将Java内存分为堆内存和栈内存,这里所指的栈内存就是指的虚拟机栈或者说是虚拟机栈中局部变量表。

局部变量表存放着各种编译期可知数据:

局部变量表存储数据的单位为slot(局部变量空间),其中64位长度的long和double占用2个slot,其余数据类型都占用一个slot。局部变量表所需要的内存在编译期就分配完成,当一个方法进入虚拟机栈时,所需要分配的局部变量空间(slot)时确定的,方法运行期间并不会改变局部变量表大小。

这里插播一条JVM对对象的访问:java通过reference 数据来操作堆上的具体对象,目前流行的对象的访问方式有两种:句柄和直接指针。

通过句柄访问对象.jpg 通过直接指针访问对象.jpg

这块区域会抛出两种异常分别是StackOverFlowError以及OutOfMemoryError:

鉴于上面2点,当虚拟机栈中由于线程创建过多导致的OutOfMemoryError问题时,可以适当的调整单个线程的大小以增加线程个数。

本地方法栈

本地方法栈与虚拟机栈相似,只不过本地方法栈时位Native方法服务的。同理这块内存区域也会抛出StackOverFlowError以及OutOfMemoryError异常

Java堆内存

堆内存是JVM管理的最大的一块内存区域,此区域存在的目的唯一目的就是存放实例对象,几乎所有的实例对象都在此分配内存,这块内存是所有线程共享的。

由于堆内存是垃圾收集器管理的主要区域,通常情况下Java堆又被称为GC堆。

当对象分配内存过程,如果堆内存没有足够的内存空间完成实例分配时,抛出OutOfMemoryError异常。

方法区

方法区与堆内存一样是所有线程共享的,用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在Hotspot虚拟机下,通常方法区被称为“永久代”。

运行时常量池

运行时常量池是方法区的一部分,Class文件中用于存储编译期生成的各种字面量和符号引用的常量池在类被加载后,将进入运行时常量池进行存储。

除了Class文件中的符号引用被爆存在运行时常量池中,一般情况下,被翻译出来的直接引用也将存储在运行时常量池中。

运行时常量池具备动态特性,除了编译期产生的常量,运行期也可以将新的常量放入池中。当运行时常量池无法在方法区申请到足够内存时,也会抛出OutOfMemoryError异常。

直接内存

直接内存并不是JVM运行时数据的一部分。

在JDK1.4版本中引入了NIO(New Input/Output)类,可以使用Native函数库直接分配堆外内存,通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,从而避免在Java堆中和Native堆中来回复制数据。

这个内存区域也会抛出OutOfMemoryError异常。

(图片源自网络)


JVM内存异常图.jpg

常量池分类

常量池概念有几个点,这里单独区分一下:

  1. 全局字符串池:这里保存的全局共享的字符串的引用,并不直接保存字符串
  2. Class文件常量池:java文件编译成.class文件后就会生成常量池,用于保存编译产生的字面量和符号引用(这里存放的只是引用)。注意:static final常量在编译期就将其结果放入了调用它的类的常量池中
  3. 运行时常量池:运行时常量池是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Class文件常量池),用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。这个常量池,每个类实例对象都能引用到。运行时常量池相对于Class文件常量池的另一个重要特征是具备动态性
参考书籍

本文摘录、整理自周志明的《深入理解Java虚拟机》一书,如想获得更详细介绍可自行查阅此书。

上一篇下一篇

猜你喜欢

热点阅读