JVM-JMM

作者: 麦大大吃不胖 | 来源:发表于2020-12-02 10:06 被阅读0次

by shihang.mai

JMM就是java内存模型,它描述了对象的内存布局、如何访问变量、线程的共享变量访问规则、对象分配等等

1. 对象的内存布局

在hotspot中,对象需要知道自己的类型,所以对象组成中有ClassPointer(类型指针)

  • 普通对象

    普通对象内存布局.png
  • 数组对象

    数组对象内存布局.png

2. 对象大小

2.1 压缩指针

运行

java -XX:+PrintCommandLineFlags -version

得到

-XX:InitialHeapSize=134217728 
-XX:MaxHeapSize=2147483648 
-XX:+PrintCommandLineFlags 
//开启压缩 类型指针
-XX:+UseCompressedClassPointers 
//开始压缩 引用类型
-XX:+UseCompressedOops 
-XX:+UseParallelGC 
java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

可以看到开启了压缩指针

  1. 压缩指针存在的意义
    我们由32位计算机变为64位,虽然我们能获得的内存大了(寻址空间从2^32 次方,变为2^64次方),但是同一个对象存在堆里会花费更多的空间(类型指针和对象引用都变为了8字节)。在64位下会带来性能问题
  • 增加了GC开销:64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少,从而加快了GC的发生,更频繁的进行GC

  • 降低CPU缓存命中率:CPU缓存是固定大小的,64位对象引用增大了,CPU能缓存的oop将相对减少会,从而降低了CPU缓存的效率

我们开启指针压缩,可以同时获得较大的内存,也能保持32位的性能

  1. 压缩指针的实现方式

首先有一个预备消息,32位CPU为什么支持最大4G?准确来说,应该是32位CPU为什么寻址容量为4G?

  1. CPU一次性读取4字节数据,即32位
  2. 4字节代表232个地址,而内存的最小IO单位是字节,其实这个232是【由8bit组成的一组的】地址数
  3. 所以实际寻址容量 = 2^32 * 8 = 2^35 = 4G

待研究,没研究清楚

2.2 对象大小举例

  • new Object():16byte

    对象头:8byte
    ClassPointer:4byte(64位计算机,原本是8字节,上面打开了-XX:+UseCompressedClassPointers压缩,所以4字节)
    padding:4字节(整个对象是8的倍数,8+4=12,+4=16)
    
  • 数组 new int[]{}:16byte

    对象头:8byte
    ClassPointer:4byte(64位计算机,原本是8字节,上面打开了-XX:+UseCompressedClassPointers压缩,所以4字节)
    数组长度:4字节
    
  • new P():32byte

    class p{
      //对象头:8byte
      //ClassPointer:4byte
      int id;//int:4byte
      String name;(64位计算机,原本是8字节,上面打开了-XX:+UseCompressedOops 压缩(oops:普通对象的指针),所以4字节)
      int age;
      byte b1;//byte:1byte
      byte b2;
      Object o;
      byte b3;
    }
    

3. 对象头具体包括什么

markword锁

不同的锁状态包括的东西不一样,但是:

  • 用2bit表示对象是否被锁定。再无锁态和偏向锁时,需要再看一bit
  • 用4bit表示GC分代年龄,4bit最大可以表示为15(2进制:1111),所以GC年龄默认15,最大15.

ps:当一个对象计算过identityHashCode(没重写hashcode,调用了默认的)后,不会进入偏向锁状态

4. 对象的定位

  • 句柄池

    句柄池
  • 直接指针

    直接指针

5. 对象如何分配

对象分配
  1. 先看能不能在栈上分配。这里涉及逃逸分析,当没方法逃逸时,即可栈上分配。
  2. 如果能在栈分配,那么分配。如果不能栈分配,那么看对象大不大,如果大,直接在old区分配。
  3. 如果不大,那么会进行TLAB在自己的线程在eden区特有的区域分配
  4. 然后经过GC回收,如果达到一定条件,进入old区

参考

https://blog.csdn.net/liujianyangbj/article/details/108074167#comments_15280876

https://blog.csdn.net/liujianyangbj/article/details/108049482

相关文章

  • JVM-JMM

    JMM(jvm内存模型) by shihang.mai 对象的内存布局 对象的创建过程class loading-...

网友评论

      本文标题:JVM-JMM

      本文链接:https://www.haomeiwen.com/subject/gojoiktx.html