Java虚拟机专题对象内存定位

来源:互联网 发布:sqlserver版本 编辑:程序博客网 时间:2024/05/21 08:37

一  对象在内存中的布局

1.1对象的创建过程

对象的创建过程可以如下图所示:


1.2 什么是符号引用和直接引用,为什么需要在常量池定位到符号的引号?

在类的解析阶段,把虚拟机常量池内的符号引号替换为直接引用。

1.2.1 符号引用(SymbolicReferences)

就是用一组符号来描述所引用的目标,符号可以是任何形式的,只要使用时能够定位到目标即可。

我们知道Java类编译成class文件,编译的时候,该类并不知道其所引用类的实际内存地址,因此只能使用符号引用来代替。

 

1.2.2 直接引用

直接饮用就是直接指向目标的指针,比如指向对象,或者类方法,类变量的指针,亦或者指向实例变量和实例方法的指针

 

二 对象内存分配方式

2.1 指针碰撞

指针碰撞就是指需要分配内存的时候,只需要将指针向空闲区域移动即可。所以堆内存空间空闲内存需要十分规整才可以。

 

2.2 空闲列表

很多时候,正在使用的内存和未被使用的内存十分不规整,分布很零散。虚拟机维护了一个空闲列表,记录哪些内存块可用,每次要分配内存时候就去空闲啊列表查询,找到一个足够的内存块去分配。

我们比较这两者分配方式,很明显指针碰撞的分配效率高于空闲列表,但是前提是空闲内存规整。如果不规整的话,空闲列表是比较适合的,比指针碰撞节约内存。

 

三 内存分配线程安全问题

假设我们线程A请求分配内存,虚拟机从空闲列表查询取出了一块内存,分配给线程A的对象,这时候还没来得及更新空闲列表,线程B也请求分配内存,也把这块内存分配给了线程B,这就存在内存分配线程安全问题。

怎么解决呢?

第一种方式:加锁(影响性能)

第二种方式:TLAB (ThreadLocal Allocate Buffer)本地线程分配缓冲区

 

四 对象结构

# Header: 主要包括自身运行时数据;类型指针

# InstanceData

# Padding

4.1 对象头(header)

对象头用于存储一些元数据,比如自身运行时数据和类型指针。

自身运行时数据(Mark Word)

# 哈希值

# GC分代年龄

# 锁状态标志

# 线程持有的锁

# 偏向线程ID和时间戳

它占用内存大小和操作系统有关,如果是32位的操作系统,那么它就是32位;如果是64位的操作系统,那么他就是64位。如果数据远远大于32位,是如何进行存储的呢?



类型指针

指向对象指向其类的元数据的指针,虚拟机通过指针来确定对象是哪一个类的实例。

 

4.2 实例数据(InstanceData)

4.3 对齐填充(Padding)

为什么需要填充?

对齐填充并不是必然的,也没什么特殊的含义,仅仅起着占位符的作用。HotSpot虚拟机内存管理规定对象起始地址必须是8字节的整数倍,比如header部分正好都是8的整数倍,如果实例数据部分大小不够8的整数倍,即没有对齐,就需要使用Padding填充

 

五 对象的访问定位

栈中的reference引用指向堆内存的地址并不一定就是对象本身。

5.1 使用句柄

并不是直接指向,而是指向堆中一块区域,叫做句柄池。句柄池里保存了实例对象的地址。

优点:栈中reference的引用地址不需要改变,改变的只是句柄池的对象实例地址

5.2 直接指针

直接指向堆中对象内存区域,HotSpot就是使用这种



阅读全文
0 0