《深入java虚拟机》读后(1)

第二章  java内存区域与内存溢出异常

一、运行时数据区域

1. 程序计数器:当前线程所执行的字节码的行号指示器。(线程私有)

2. java虚拟机栈:描述java方法执行的内存模型。(线程私有)

    每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程。

3. 本地方法栈:描述虚拟机使用到的native方法的内存模型。(线程私有)

4. java堆:存放对象实例。垃圾收集器管理的主要区域。

5. 方法区:存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。

    运行时常量池是方法区的一部分。用于存放编译时期产生的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放。(具备动态性)

6. 直接内存:本机直接内存。

二、 HotSpot虚拟机中对象

1. 对象的创建

    1)虚拟机遇到一条new指令。

    2)检查这个指令的参数能否在常量池中定位到类的符号引用,并检查这个符号引用代表的类是否被加载、解析和初始化过。

    3)给对象分配内存。(在类加载完成后已确定所需内存大小)

    4)虚拟机将分配到的内存空间初始化为零值。(确保对象的实例字段在java代码中可以不赋初始值就直接使用)

    5)设置对象(对象头)

    6)执行<init>方法

注:两种方案解决分配空间的线程安全问题:

(1)分配内存空间的动作进行同步处理——虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。

(2) 每个线程在java堆(Eden空间)中预先分配一块内存——本地线程分配缓冲(TLAB),哪个线程需要分配内存就在哪个TLAB分配,只有TLAB用完了需要分配新的TLAB时才需要同步锁定。同时虚拟机初始化内存空间这一操作可以提前至TLAB分配时进行。

2. 对象的内存布局

    1)对象头

        第一部分用于存储对象自身的运行时数据(Mark Word),极小存储空间。

        第二部分是类型指针,指向他的类元数据,用来确定他是哪个类的实例(非必需保留类型指针)。

        另外,如果对象是数组,对象头中必须有一块记录数组长度的数据,因为虚拟机不知道分配多少内存。

    2)实例数据:对象真正存储的有效信息,也是程序代码中所定义的各种类型的字段内容。

    3)对齐填充:补全对象大小至8字节的倍数。

3. 对象的访问定位(两种访问方式)

    第一种:句柄访问。在java堆中划分一块内存作为句柄池,reference中储存句柄池地址,句柄中包含了对象实例数据(java堆)和类型数据(方法区)的地址。

    优势:在对象被移动(gc)时只会改变句柄中的实例数据指针。

    第二种:直接指针访问。reference中存储对象的地址。

    优势:快,节约了一次指针定位的时间。

        



猜你喜欢

转载自www.cnblogs.com/lc4j/p/11013506.html