JVM学习笔记-运行时数据区

时间:2024-01-01 16:41:15

  不同于C,C++程序,Java程序的内存管理工作由Java虚拟机(JVM)接管,这减低了java程序员的负担,但如果出现内存泄露与溢出问题如报OutOfMemory,*异常错误时,如果不了解JVM虚拟机的内存管理细节,往往很难快速定位错误。

  JVM在运行时会把其所管理的内存分为几个不同的数据区域,分别为:程序计数器,虚拟机栈,本地方法栈,堆,方法区等。这些区域存放的数据不同,功能也不同。

  JVM管理的内存包含以下几个运行时数据区:

JVM学习笔记-运行时数据区

1.程序计数器

  程序计数器是一块较小的内存空间,可将其视为当前线程所执行字节码的行号指示器。为了线程切换后能恢复到正确执行的位置,每条线程都需要有单独的程序计数器,使各线程之间互不干扰,独立存储。因此该区域是线程私有区。

  值得一提的是,该区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError异常的区域。

2.Java虚拟机栈

  Java虚拟机栈也是线程私有的。虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至执行完成的过程,就是一个栈帧在虚拟机栈中入栈出栈的过程。

  局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,int,short,float,double,long),对象引用)和returnAddress(指向一条字节码指令的地址)类型。局部变量表所需内存空间在编译期间完成分配。

  在Java虚拟机规范中,该区域规定了两种异常状况:若果线程请求的栈深度大于虚拟机允许的深度,将抛出*异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfemory异常。

3.本地方法栈

  本地方法栈与虚拟机栈发挥相似你的作用,本地方法栈为虚拟机用到的native方法服务。有的虚拟机直接将本地方法栈与虚拟机栈合并。本地方法栈也会抛出*与OutOfMemoryError异常。

4.Java堆

  Java堆是我们最熟悉的一块区域,它是Java虚拟机所管理的最大的一块内存。Java堆是由所有线程共享的一块内存区域,在虚拟机启动时创建。该内存区的唯一目的就是存放对象实例,几乎所有的对象实例都在Java堆分配内存。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。在实际中,Java堆是可扩展的,一般有参数:-Xmx(堆最大值)    -Xms(堆最小值)控制。如果在堆中没有内存完成实例分配,且堆也无法再扩展时,会抛出OutOfMemoryError异常。

5.方法区

  方法区也是各个线程共享的内存区,它用于存储被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常。

6.运行时常量池

  运行时常量池是方法区的一部分。Class文件中除了有类的版本,字段,方法,接口等信息外,还有一项信息是常量池(constant pool),用于存放编译期产生的各种字面量和符号引用,该部分内容在类加载后进入方法区的运行时常量池中存放。运行时常量池相对于Class文件的常量池的一个重要特征就是具备动态性,即并不要求常量一定只在编译期产生,运行期间也可将常量放入池中。String类中的itern()方法就是这样一个例子,当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。它遵循以下规则:对于任意两个字符串 st,当且仅当 s.equals(t)true 时,s.intern() == t.intern() 才为 true。 当常量池无法再分配到足够的内存时将抛出OutOfMemoryError异常。

7.直接内存

  直接内存并不是虚拟机运行时数据区的一部分,也非虚拟机规范定义的内存区,但该部分内存会被频繁使用。直接内存不会受到虚拟机内存大小的限制,但会受本机内存大小的限制。因此也可能会抛出OutOfMemoryError异常。