首页 > 代码库 > 深入JVM读书笔记(一)——jvm数据区基础知识

深入JVM读书笔记(一)——jvm数据区基础知识

  最近得空,就把《深入理解Java虚拟机》重新看了一遍,特写下现在的读书笔记,总结知识点,记录现在的理解,便于以后的回顾。下面的内容也会按照这本书的章节来划分知识点!

  Let‘s go!

  

  想要了解Java虚拟机,一定要先明白Java运行时划分为哪些数据区域,具体的可以参考下图,按照是否为线程私有可以划分为:

  线程私有:虚拟机栈、本地方法栈、程序计数器

  线程共有:方法区、堆

   

下面详细说一下各个数据区的作用:

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

  程序计数器是一块较小的内存空间,用于标识线程当前的所执行的字节码的行号。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。各条线程之间互不影响,独立存储。

2. 虚拟机栈

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

  会出现两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深读,抛出SO(StackOverflow)异常;如果虚拟机站可以动态扩展无法申请到足够的内存时会抛出OOM(OutOfMemory)异常;

3. 本地方法栈

  类似于虚拟机栈,不同的是虚拟机栈面对的是Java方法服务,而本地方法栈则面对Native方法服务。在Sun HotSpot中,直接把两者实现合二为一。也会抛出SO和OOM。

4. 堆

  此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的主要区域,因此也被称为GC堆。如果从垃圾回收的“分代收集算法”的角度来看,Java堆还可细分为:新生代、年老代、Eden空间、From Survivor空间和To Survivor空间。

  如果堆没有内存来完成实例分配,并且堆也无法再扩展时,会抛出OOM异常,可以通过-Xmx和-Xms控制。

5. 方法区

  用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在HotSpot上很多人把方法区称为“永久代”(Permanent Generation),是因为HotSpot把GC分代收集扩展至方法区。也会抛出OOM。

  除了以上这些数据区,还有一些常见的名字,下面也解释一下:

  运行时常量池(Runtime Constant Pool):用于存放Class文件加载后产生的常量表(Constant Pool Table),并不要求一定是编译期产生的,运行期也可能产生新的常量放入其中。例如String的intern()方法,也会抛出OOM。

 

  下面说一下最重要的,也是最基础的:在Java中是如何实现对象访问的,会涉及到Java栈、Java堆和方法区这三个区域。

  现在主流的访问方式有两种:使用句柄和直接指针。

  1. 使用句柄

  在Java堆中划出一块内存作为句柄池,在栈中的reference存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的地址信息。

  优势:reference中存储的是稳定的句柄地址,在对象被移动时,只用改变句柄中的实例数据指针,而reference本身不需要修改。

  实现如下图所示:

   

  2. 直接指针

  Java栈中reference直接存储的就是对象地址,而Java堆中对象放置访问类型数据的相关信息,Sun HoSpot采用的就是这种方式。

  优势:速度更快,节省了一次指针定位的时间开销,对于Java频繁的对象访问,这也是很可观的。

  实现如下图所示:

  

深入JVM读书笔记(一)——jvm数据区基础知识