JVM 学习笔记

文章目录

  • 第二部分 自动内存管理
    • 第2章 Java内存区域与内存溢出异常
      • 2.2运行时数据区域
        • 线程隔离
        • 线程共享
      • 2.3HotSpot虚拟机对象探秘
        • 对象的创建
        • 对象的内存布局
        • 对象访问定位

第二部分 自动内存管理

第2章 Java内存区域与内存溢出异常

2.2运行时数据区域

JVM 学习笔记_第1张图片

线程隔离

  1. 程序计数器PC
    可以看做是当前线程所执行的字节码的行号指示器。
    如果线程正在执行Java方法,PC记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是一个本地(Native)方法,值为Undefined。
    唯一一个不会出现OutOfMemoryError情况的区域。
  2. Java虚拟机栈
    每个方法被执行时,Java虚拟机会创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
    每个方法从调用到执行完毕对应着一个栈帧入栈到出栈的过程。
    局部变量表中存放了基本数据类型(8种,char、byte、short、int、long、float、double、boolean)、对象引用(reference类型,并不是对象本身)和returnAddress类型(指向一条字节码指令的地址)。
    会抛出StackOverflowError和OutOfMemoryError异常
  3. 本地方法栈
    为虚拟机使用到的本地方法提供服务。
    会抛出StackOverflowError和OutOfMemoryError异常

线程共享

  1. Java堆
    唯一的作用是存放对象实例
    并不是所有对象都分配到堆中
    当Java堆中没有内存可用于对象实例分配,并且堆也无法拓展时,抛出OutOfMemoryError异常。
  2. 方法区
    用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
    当方法区无法满足新内存分配需求时,抛出OutOfMemoryError异常
  3. 运行时常量池
    是方法区的一部分

2.3HotSpot虚拟机对象探秘

对象的创建

类加载检查 ==》分配内存 ==》将分配到的内存空间(不包括对象头)初始化零值 ==》对对象进行设置 ==》执行()方法

两种分配内存的方式

  1. 指针碰撞:
    JVM 学习笔记_第2张图片

  2. 空闲列表:
    JVM 学习笔记_第3张图片
    当使用Serial、ParNew等带压缩整理过程的收集器时,采用指针碰撞。
    当使用CMS这种基于清除(sweep)算法的收集器,采用空闲列表。

对象创建的线程安全问题

  1. 采用CAS配上失败重试的方法保证更新操作的原子性。
  2. 把内存分配的动作按照线程划分在不同的空间之中,即每个线程预先分配好一小块空间,称为本地线程分配缓存(TLAB),哪个线程需要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区内存用完了,需要分配新缓冲区时才需要同步锁。

对象的内存布局

  1. 对象头(Header)
    1. 存储对象自身运行时数据:哈希码、GC分代年龄、锁状态标志等
    2. 类型指针:执行类型元数据的指针,通过这个指针确定对象是哪个类的实例。(并不一定需要保留)
    3. 如果是Java数组,必须有一块用于记录数组长度的数据。
  2. 实例数据
    对象真正的有效数据,无论是我们定义的字段还是从父类继承还是子类中定义的字段都必须记录起来。
    相同宽度的字段总是别分配到一起存放,在满足这个条件的前提下,父类定义的变量会出现在子类之前。
  3. 对齐填充
    不是必然存在,只起到占位符的作用。

对象访问定位

  1. 通过句柄访问对象
    JVM 学习笔记_第4张图片
    Java堆中划分出一块内存作为句柄池,reference中存放的是对象的句柄地址,句柄中包含了对象实例数据和对象数据类型的地址信息。
  2. 直接指针访问
    JVM 学习笔记_第5张图片
    reference中存放的是对象地址。

对比:
句柄:reference中存储的是稳定的句柄地址,对象移动时,只改变句柄中的实例数据指针, reference不变。
直接指针:就是速度更快,节约了一次指针定位的开销。

你可能感兴趣的:(JVM 学习笔记)