深入理解Java虚拟机(2)对象创建及内存分配

一.对象的创建过程

虚拟机遇到一条new指令时:
1. 首先去常量池寻找符号引用(通过引用找到并检查该类是否被加载,解析)
2. 未被加载过则去加载解析类
3. 为新生的对象分配内存(此时对象所占内存大小确定)
4. 分配的堆内存空间初始化(即赋零值)
5. 设置对象头信息(header) — 对于JVM来说,此时已经初始化完成
6. java 类实例的<-init-> 方法,初始化 — 对于应用层正式可用

二.对象内存分配规则

假设Java堆中内存是绝对规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针指向空闲空间那边挪动一段与对象大小相等的距离,这个分配方式叫做“指针碰撞”。
  如果Java堆中的内存并不规整,已使用的内存和空闲的内存相互交错,那就没办法简单的进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”。
  选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有并使用压缩整理功能决定,既选择回收方式compact还是mark-sweep

分配过程中的线程安全解决方案:

1, 通过CAS锁,也就是分配内存时进行同步操作保证线程安全
2,TLAB ThreadLocalAllocationBUffer 利用堆内存中的私有内存缓冲区,每个线程分配自己线程的私有内存,私有内存不够才进行同步操作,线程之间分配内存互相不影响
  

三.对象的内存模型

  在Hotspot虚拟机中,对象在内存中储存的布局可以分为3块区域:对象头、实例数据和对齐填充

对象头:

  • 储存对象自身的运行时数据,如哈希码,GC分带、锁状态标志、线程持有的锁、遍向线程ID、偏向时间戳
  • 储存指类型指针:即对象指向它的类元数据的指针,虚拟机通过这么指针来确定这个对象是哪个类的实例
     

四.对象的访问

1,使用句柄访问

  Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址。
  优势:reference中存储的是稳点的句柄地址,在对象被移动(垃圾收集时对象是非常普遍的行为)时,只会改变句柄中的实例数据指针,而reference本身不需要修改。

2,使用直接指针访问

  reference中存储的直接就是对象的地址,如常用的对象调用。
  优势:速度更快,节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。

猜你喜欢

转载自blog.csdn.net/iamcodingmylife/article/details/79885967