JVM(九)指针压缩原理-计算对象大小

前言:

Oop-Klass体系回顾,在JVM第一篇中讲过了这部分内容,今天的内容也涉及,回顾一下。

ooPDesc

       ---------MarkOopDesc:存放锁的信息,分代年龄等等

       ---------InstanceOopDesc:非数组对象

  ---------arrayOopDesc:数组对象

    -----typeArrayOopDesc:基本数据类型数组,对应的有个存放基本数据类型数组元信息的TypeArrayKlass。

    -----objArrayOopDesc:引用数据类型数组,对应有个objArrayKlass存放引用类型的元信息。

我们这里再举个例子,通过HSDB查看在JVM中的对象。

 

 启动HSDB,在jdk1.8/lib下执行:java -cp  sa-jdi.jar sun.jvm.hotspot.HSDB

  选择main线程,打开stack memroy

第一个就是我们定义的Position对象的地址,第二个是字节数组,第三个是char数组。

 

我们Inspector查看Position对象。第一部分就是Oop,在对象里面有元数据类型的InstanceKlass。

这里有个重要的点就是:_layout_helper参数。

1:如果是非数组对象,这个大小指的就是类生成的对象的大小。

2:如果是数组对象,是个负值。

3:等于0的话,?

 

 内存布局

 我之前写过一篇博客里面提到了这部分内容:https://blog.csdn.net/A7_A8_A9/article/details/105730007?spm=1001.2014.3001.5501

  

 

 

 

 补充:针对上面的内存布局,这里有几点需要说明。

1:对象头中类型指针-class pointer它是指向instanceklass在方法区的地址,如果开启指针压缩的情况下是占4B,如果不开启是占8B。

2:如果对象不是数组,对象头中的数组长度占0B。是数组的话占4字节,因此数组的长度最大为2的32次方-1.

3:实例数据中char类型数据在c++中是用short表示的,所以占两个字节。

4:实例数据中的引用数据类型,如果开启指针压缩占4B,不开启指针压缩占8B。

计算对象大小

指针压缩从jdk1.6之后是默认开启的。可以通过参数控制。

-XX:+UseCompressedOops/-XX:-UseCompressedOops

可以引用:这个依赖来打印对象布局大小。

<dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.10</version>
        </dependency>

 

1:没有实例数据的对象

测试代码:

public class TestClass {
    public static void main(String[] args) {
        TestClass tes1= new TestClass();
        System.out.println(ClassLayout.parseInstance(tes1).toPrintable());
    }
}

结果:一共占16字节。

 

 我们算下,这个16是怎么来的。其实很简单就是:对象头8B+类型指针(压缩)4B+数组长度0B+实例数据0B+4B(对齐填充)=16B

 不开启指针压缩就是:对象头8B+类型指针(不开启指针压缩)8B+数组长度0B+实例数据0B=16B  没有了对齐填充也是16B

2:有实例数据

我们给TestClass加两个实例属性。

public class TestClass {
    int i=2;
    String name="阿三";
    public static void main(String[] args) {
        TestClass tes1= new TestClass();
        System.out.println(ClassLayout.parseInstance(tes1).toPrintable());
    }
}

开启指针压缩:

Mark Word 8B+类型指针4B(压缩)+数组长度0B+实例数据int 4B+引用数据类型4B(压缩)+4B(对齐填充)=24B

我们看下运行结果:

关闭指针压缩:

对象头8B+类型指针8B(压缩)+数组长度0B+实例数据int 4B+引用数据类型8B(压缩)+4B(对齐填充)=32B

运行结果:

 

 指针压缩

开启指针压缩是为了节省内存,寻址效率有些提高。

指针压缩的原理:

假如分配的内存从0开始且顺序存储,三个对象分别: test1=16B  test2=24B  test3=32B, 三个地址分别:test1=00000, test2=16(十进制)  /10000(二进制),test3=40(十进制) 101000(二进制)。

我们都知道java中的对象都是8字节对齐的,8字节对齐有一个特点就是 1 000,发现了吗 所有对象的指针后三位总是0。这就是指针压缩的点。

压缩原理就是两句话:

1:存储的时候,后三位抹除0.

      就变成:test1=00,test2=10

2:使用的时候,后三位补0.

它的指针不再表示对象在内存中的精确位置,而是表示 偏移量 。这意味着 32 位的指针可以引用 40 亿个 对象 , 而不是 40 亿个字节。最终, 也就是说堆内存增长到 32 GB 的物理内存,也可以用 32 位的指针表示。

使用HSDB查看指针压缩现象:

示例代码:

public class TestClass {
    public static void main(String[] args) {
        Position position = new Position(1, 2, 3);
        while(true);
    }
}

关闭指针压缩:

 

 

 对象地址:

 

 打开指针压缩:

 

对象大小明显变小了,而且klass属性也不一样了。

这是因为在OopDesc中,指针压缩和不压缩klass存储在不同的地方。

 

 

 

JVM调优基础知识

1:上线前对JVM进行预估调优

2:上线后小规模调优

3:OOM,full GC 频繁调优

主要调什么?

1:方法区

2:虚拟机栈

3:堆区

4:热点代码缓冲区

 

posted @ 2021-02-01 23:37  蒙恬括  阅读(978)  评论(0编辑  收藏  举报