对象在内存中的存储布局

对象存储在堆中,对象的引用在栈中,对象在内存中的存储布局如下:

  1. 对象头
    • mark word
      • GC分代年龄
      • 锁状态标记
      • 哈希码
    • 类型指针
    • length(只有数组对象包含)
  2. 实例数据
  3. 对齐填充

对象的创建过程

  1. 类加载
  2. 内存分配
  3. 初始化零值
  4. 实际的初始化
  5. 返回引用

内存分配方式

  1. 指针碰撞 : 用于Serial和ParNew等不会产生内存碎片的垃圾收集器
    • 为了解决多线程并发问题,引入TLAB。
    • TLAB:预先为每个线程分配一块较大的空间,这样每个线程直接在自己的TLAB中进行内存分配,就不用考虑多线程的问题了!
    • 可以用CAS为TLAB分配内存!
  2. 空闲列表: CMS垃圾收集器就是采用的这种方法

对象定位方式

  1. 句柄方式
    • 在堆中划分一块区域作为句柄池,句柄中包含了类数据地址和实例数据地址
    • 好处是对象位置发生改变时,只需要改变句柄就好,而不需要改变栈中的引用
    • 坏处是访问麻烦
  2. 直接引用:HotSpot采用的时直接引用的方式。
    • 引用直接指向对象的实例数据地址(实例对象的对象头总包含类数据地址)
    • 好处是访问简单
    • 坏处是对象位置改变时,需要改变栈中的引用。

对象空间分配过程

  1. 尝试栈上分配
    • 逃逸分析:对象的作用范围只局限在方法内部
  2. 大对象可直接分配到老年代
  3. 尝试TLAB分配
  4. 伊甸分配

指针压缩

  1. 在64位平台的HotSpot中使用64位(8字节)指针,会占用更多内存,而且使用较大指针在主内存和缓存之间移动数据会占用较大宽带,GC也会承受更大压力
  2. 为了减小64位平台下的内存消耗,启用指针压缩功能
  3. 堆内存小于4G时,不需要启动指针压缩,jvm会直接除去高32位地址,使用低虚拟地址空间
  4. 堆内存大于32G时,压缩指针会失效,会强制使用64位来对java对象寻址
  5. 指针压缩原理:编码、解码
  6. 哪些信息会被压缩
    • 对象的全局静态变量
    • 对象头信息:64位平台下,原生对象头大小位16字节(markword 8字节,类型指针8字节),压缩后为12字节(markword 8字节,classpointer 4字节)
    • 对象的引用类型,由8字节压缩至4字节
    • 对象数组类型,由24字节压缩到16字节
  7. 哪些信息不会被压缩
    • 指向非Heap对象的指针
    • 局部变量、传参、返回值、Null指针

Object o = new Object()在内存中占用多少字节

  1. 64位平台下,采用压缩指针:16字节:8(mark word) + 4(class pointer)+4(对齐)
  2. 64位平台下,不采用压缩指针:16字节:8(mark word)+8(class pointer)
  3. o也占4个字节,所以满分答案是20字节。

HotSpot为什么不使用C++对象来代表java对象?

  1. 为了避免在每个对象中都含有一个虚函数指针。
  2. oop中不含任何虚函数,虚函数都在Klass对象中
  3. 在C++中,非虚函数在编译时就已经确定调用目标。
  4. 为了实现多态,也就是通过基类的指针调用子类的方法,才引入了虚函数
  5. 在C++中,只有虚函数才能被重写!java中的每个函数都是虚函数。

Class对象位于堆,还是方法区?

  1. 位于方法区。??应该位于堆中吧?
  2. 但是Class对象还会在堆区生成一个对象用于反射!