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

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