JVM内存结构

JAVA内存区域

JVM在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区等。前三者为线程私有的,后两者为线程共享区域。

JVM内存结构_第1张图片
深入理解Java虚拟机(第二版)P39

1. 程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码行号指示器。
因为JVM的多线程是通过线程轮换并分配处理器执行时间来实现的,所以在任意时刻,一个处理器都只会执行一条线程中的指令。所以为了切换后能够恢复到正确的执行位置,每条线程都需要一个独立的计数器,它们互不影响。因此,程序计数器是线程私有的。
如果线程执行的是一个Java方法,计数器记录正在执行的虚拟机字节码指令的地址。如果是Native方法,能为空。

2. Java虚拟机栈(Java Virtual Machine Stacks)

Java虚拟机栈描述的是Java方法执行的内存模型: 每个方法执行的时候会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。一个方法从执行到返回,就对应一个栈帧在虚拟机栈中入栈和出栈的过程。

局部变量表

局部变量表存放的是编译期可知的各种基本数据类型、对象引用(reference类型)、returnAddress类型。其中64位长度的long和double类型数据会占用2个局部变量空间(Slot),其余只占用1个。局部变量表所需空间在变异期间完成分配,所以当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间完全确定,运行期间不会改变。

3. 本地方法栈(Native Method Stack)

本地方法栈和虚拟机栈的作用非常相似、区别在于一个执行的是Java方法,一个执行的是Native方法。
使用native关键字表示的方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由Java去调用。

4. Java堆(Java Heap)

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。这个内存区域的唯一目的就是存放对象实例以及数组。Java堆是垃圾收集器的主要区域。从内存回收的角度来看,现代收集器都是使用分代收集算法,所以可以细分为:新生代和老年代,新生代还可以分为Eden空间、From Survivor空间、To Survivor空间等。
Java堆的JVM参数主要是 -Xms和-Xmn。

5. 方法区(Method Area)

方法区和Java堆一样,是各个线程共享的内存区域,它被用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译期编译后的代码等数据

运行时常量池(Runtime Constant Pool)

运行时常量池是方法区的一部分。 Class文件中除了类的版本、字段、方法、接口等描述信息,还有常量池(Constant Pool Table),主要包括编译期生成的各种字面量(Literal)和符号引用量(Symbolic References)。字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。包括了如下三种类型的常量:

  • 类和接口的全限定名
  • 字段名称和描述符
  • 方法名称和描述符
    这部分内容在类加载后进入方法区的运行时常量池存放。
    运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。
    常量池的好处
    常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
    例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
  • 节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
  • 节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
    双等号==的含义
  • 基本数据类型之间应用双等号,比较的是他们的数值。
  • 复合数据类型(类)之间应用双等号,比较的是他们在内存中的存放地址。

你可能感兴趣的:(JVM内存结构)