JVM之运行时数据区

471 阅读3分钟

JVM概述

JVM是Java Virtual Machine(java虚拟机)的简写,是个概念。java每个版本发布的时候都会发布对应版本的java虚拟机规范The Java® Virtual Machine Specification定义了java虚拟机的各种规范信息:

oracle官网上可以找到各个版本的jvms,基于jvms有不同的jvm实现,我们常说的jvm指的都是HotSpot的jvm实现,也是oracle JDK的默认实现,除了HotSpot外还有其他的jvm实现:

  • J9-IMB
  • Microsoft VM
  • TaoBaoVm
  • BEA
  • ...

此外要注意区分jvm和java的关系,java是一门编程语言,而JVM是负责解析和运行二进制.class文件的,两者并不是绑定死的,可以基于.class文件规范实现另外一套编程语言,比如scala。

运行时数据区

根据java虚拟机规范,运行时数据区主要分为程序计数器、虚拟机栈、方法区、运行时常量池和本地方法栈这六个部分,具体如图:

程序计数器

程序计数器,直译是PC寄存器。jvm支持同时运行多个线程,每个线程都有自己自己的程序计数器。当执行的方法不是本地方法时,程序计数器中记录着当前执行的指令地址,如果是本地方法则为undefined。程序计数器足以保存方法返回地址或者本地指针,是jvm中唯一一个没有规定任何OOM情况的区域。

虚拟机栈

每个线程都独享一个虚拟机栈,在线程创建的时候一起创建。在栈中存放着叫做栈帧的东西,每个方法在执行的时候都会创建一个栈帧,存放着以下信息:

  1. 本地变量表,数组存放在局部变量,编译时决定大小;
  2. 操作数栈,LIFO,编译时决定大小;
  3. 动态链接,简单来说就是运行时class中方法的引用;
  4. 方法返回:
    1. 正常返回;
    2. 中断返回。

虚拟机栈大小可以是固定的也可以是动态的。当虚拟机栈申请的内存超过允许的最大限制时会报StackOverflowError,当JVM剩余的内存不够虚拟机栈申请合理的内存时报OOM错误。

堆是所有线程共享的一块内存区域,在虚拟机创建的时候一同创建。堆中存放着所有对象的数据,是GC主要工作的区域。当申请不到内存时报OOM错误。

方法区

方法区也是所有线程共享的区域,存放着类信息,运行时常量池,方法数据和方法代码等。在虚拟机创建时一同创建。方法区在逻辑上也是堆的一部分,jvms允许简单实现不对该区域进行垃圾回收,为了与区别通常也叫非堆

在HotSpot的jvm中,用永久代来实现方法区,并将GC的回收延伸至方法区主要回收常量池和类型卸载。在jdk1.7中以将字符串常量池移动到堆中,jdk1.8中取消永久代并引入元空间使用本地内存来实现方法区。

运行时常量池

class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到常量池中。比如基本类型的包装类常量(浮点类型没有常量池,Integer常量池范围为[-128, 127])

本地方法栈

和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。 也会抛出StackOverflowError和OutOfMemoryError。