qingliu

@qingliu

hello world

227 words

Guestbook
You'll only receive email when qingliu publishes a new post

Java 创建对象

1.创建对象

1.去常量池中找对于类的符号引用--->检查类是否被加载,解析,初始化过
2.进行类加载
3.加载完之后进行对象堆内存分配(大小在类加载完之后就已知),分配根据内存是否规整,分为指针碰撞(就是一个分界线)和空闲列表,规整由 GC 收集器是否压缩决定,Serial,ParNew 是指针碰撞,CMS 为空闲列表
4.处理对象分配时多线程的对象指针,CAS + 重试还有 TLAB (本地线程分配缓冲)保证每个线程在自己的 TLAB 分配对象
5.对对象分配的区域进行初始化零值,一些默认类型的值
6.设置对象头,包含类的 metadata,对象 GC 分代年龄等
7.执行对象的 init 方法

2.对象内存布局

包含三部分: 对象头,对象实例数据,对齐填充

2.1 对象头

  • 第一部分:运行时数据
内容 状态
对象哈希,分代年龄 未锁定
指向锁记录的指针 轻量级锁定
指向重量锁指针 锁膨胀(重量锁锁定)
GC 标记
偏向线程 ID,时间戳,对象分代年龄 可偏向锁
  • 第二部分:类型指针

对象指向类 metadata 的指针,通过此指针确定对象属于哪一个类实例

2.2 存储对象的内容

包括每个字段的内容,基本类型,oops( ordinary object pointers)

2.3 填充部分

HotSpot VM 内存管理系统要求对象起始地址都是 8 byte 的整数倍。

3.对象访问

java 栈中本地变量表存储一个 reference,通过 reference 来访问对象

  • 句柄

堆内维护一个句柄池,reference 指向对象句柄地址,句柄内容如下:

句柄内容 指向位置
到对象实例的数据的指针 堆中实例池中的对象实例数据
到对象类型数据的指针 方法区中对象类型数据
  • 直接指针

reference 直接指向堆中对象地址,java堆中对象布局包含了类型数据的指针便于访问方法区对象类型数据
HotSpot VM 采用直接指针方式,速度快减少了指针定位。

CPU Cache 与 MESI

CPU 缓存

由于CPU 在计算之前存在取指-->译码-->执行 三个阶段,在计算之前需要将前两步准备好,并且准备好操作数,这样才能够最大效率的利用 CPU,但是CPU 的存储读写速度与主存存在巨大的差距,则引入 cache 来消减这个差距,cache 使用的是 SRAM ,SRAM 是 Static Random Access Memory 静态RAM 集成度高速度快,,他比我们的 主存(DRAM)速度快所以用其来做 CPU L1 L2 L3(速度依次降低) 等缓存

Cache 一致性问题

主存中的内存块是按照缓存行映射加载到 cache 中的,缓存行由 如下接口组成

状态 地址 数据

对于多核 CPU 来说每个核都有自己对应的 L1,L2 缓存,L3共享缓缓存,那同一块内存快就有可能被多个CPU 加载到自己的缓存中进行数据的操作,那就会带来缓存一致性问题,如何保证数据内存快的数据一致是需要解决的问题

MESI 协议缓存一致性解决方案

CPU 修改 cache 中的数据后对数据的处理:

  1. wirte through :每次修改之后立即更新到主存,那每次写共享数据就会导致总线事务,高一致性,但是效率低
  2. wirte back: 每次修改后不会立即更新到主存,而是等缓存行在某个时机的时候写回

上述两种方案多线程环境下都需要处理缓存一致性问题,CPU 处理缓存一致性问题提供两种策略
写失效:一个CPU 修改了数据,则该数据在其他CPU 未失效
写更新:一个CPU 修改了数据,则更新到其他 CPU 的该数据

MESI 协议由 Modify,exclusive,shared, invalid 四种状态,表示 修改,独占,共享,失效

Modify:当前 CPU 有最新数据,其他 CPU 该数据失效,用的就是写失效策略
Exclusive:只有当前 CPU 由数据,其他 CPU 没有该数据 和主存数据一致
Shared:当前 CPU 和 其他 CPU 由共同的数据但和主存一致
Invalid:当前数据为失效,需要从主存读取

由于上述四种状态对其他 CPU 都是可感知的,就可以调整自己对 cache 的操作。