Fork me on GitHub

读薄《深入理解 JAVA 虚拟机》Java内存区域

很早之前看了《深入理解 JAVA 虚拟机》并写下了读书笔记。最近在结合一些其他资料整理博客。希望能帮助到其他人抓住书的重点。
Java运行时数据区域

Java运行时数据区域

白色为线程独占的,灰色为线程共享的。

Java在运行的时候会把他所管理的内存划分为若干区域,经常有人把内存区域分为堆内存和栈内存,这种内存分发比较粗糙,下面我们来仔细介绍一下 Java 中的内存区域。

程序计数器

程序记录器是用于记录程序所运行到的位置的,程序工作的时候就是通过改变这个计数器的值来选择下一条需要执行的指令(类似计算机组成原理中的 PC 计数器)。

如果线程正在执行一个 Java 方法,则计数器记录了正在执行的指令的地址。如果在执行一个 Native 方法,则这个寄存器计数值为空,注意这是唯一一个没有规定任何OutOfMemoryError的区域。

Java虚拟机栈

每一条线程都要有独立的 Java 虚拟机栈每一个方法的执行都会创建一个栈帧,方法执行结束以后栈帧就会被弹出,这个栈帧储存了局部变量表,操作数栈,动态链接,方法出口。可以类比于在汇编语言中转跳到一个方法时需要用pushad来保存寄存器中的值,而这一串保存在寄存器中的值就是一个栈帧。

局部变量表

局部变量表是栈帧的一部分,里面存放了各种基本数据类型和对象引用。,因为除了long和double会占用两个局部变量空间,其他的都是占用一个局部变量空间,所以每个方法需要分配多少局部变量空间是确定的,而且是在编译的时候确定的。

在Java虚拟机中,如果线程请求的栈深度大于虚拟机所允许的深度,则会丢出StackOverflowError

本地方法栈

与 Java 虚拟机栈的区别就是它是为 native 方法服务的。有些虚拟机会将他们合二为一。

Java堆

new 出来的对象存储区域,GC 主要发生在这个内存区域上。

方法区

也被称为永久代。 用于存储虚拟机加载的类信息,常量,静态变量,即时编译以后的代码。方法区里对于对象的回收的对类型的卸载条件非常苛刻,回收效益不大,但是还是有必要回收的。

运行时常量池

方法区的一部分,存放编译器生成的各种字面量(1,2,“abstract”)和符号引用。

一般来说除了符号引用,直接引用也会保存在这个地方。

运行常量池相对于Class文件常量池另外一个重要的区别就是具备动态性,Java 运行中可能动态产生常量,比如String 类的 intern() 方法。

对象的创建

创建对象分为这几部

  1. 首先去检查这个类有没有被加载过,如果没有则加载该类。
  2. 在加载完类后,对象分配内存,内存大小在类加载完成后已经可以确定。
  3. 内存分配完成后将分配到的内存空间置为 0,这一步保证对象实例字段可以不用赋值直接使用
  4. 填充对象头

对象的内存布局

对象在内存区域中

[ [ mark word, 类型指针 ],实例数据,对齐填充 ]

对象头

对象头由 mark word 和类型指针组成,为了虚拟机的效率,Mark Word 会根据对象的状态复用存储空间。通过标志位来表示当前存储的是什么。

实例数据

实例数据部分存储对象有效信息,HotSpot虚拟机默认分配策略为

long/double,int,short/char,byte,boolean。

posted @ 2018-11-05 21:29  zjmeow  阅读(227)  评论(0编辑  收藏  举报