数据类型

  • 基本数据类型
  • 引用数据类型

基本数据类型

数据类型大小
boolean1 bytes
byte1 bytes
short2 bytes
char2 bytes
int4 bytes
long8 bytes
float4 bytes
double8 bytes
  • 如果再方法体内定一,即在栈上分配
  • 如果是类的成员变量,即在堆上分配
  • 如果是类的静态成员变量,即在方法区上分配

引用数据类型

除了对象本身之外,它还存在一个指向他的引用(指针)
指针占用的内存在64位虚拟机上8个字节
如果开启指针压缩是4个字节

压缩指针规则

在堆中,32位的对象引用(指针)占4个字节,而64位的对象引用占8个字节。64位JVM在支持更大堆的同时,由于对象引用变大却带来了性能问题:

  1. 增加了GC开销:64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少,从而加快了GC的发生,更频繁的进行GC
  2. 降低CPU缓存命中率:64位对象引用增大了,CPU能缓存的oop将会更少,从而降低了CPU缓存的效率。

为了能够保持32位的性能,oop必须保留32位。那么,如何用32位oop来引用更大的堆内存呢?
答案是——压缩指针(CompressedOops)。

OOP:ordinary object pointer

普通对象指针
启动CompressOops后
会压缩的对象:

  1. 每个class的属性指针(静态成员变量)
  2. 每个对象的属性指针
  3. 普通对象数组的每个元素指针

不会压缩的对象:

  1. 指向PermGen的class对象指针
  2. 本地变量
  3. 堆栈元素
  4. 入参
  5. 返回值
  6. NULL指针

开启压缩

-XX:+UseCompressedOops

关闭压缩

-XX:-UseCompressedOops

打印jvm参数

-XX:+PrintFlagsFinal

winows打印jvm参数

java -XX:+PrintFlagsFinal -version |findstr UseCompressedOops

PS C:\Users\Think> java -XX:+PrintFlagsFinal -version |findstr UseCompressedOops
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
     bool UseCompressedOops                        := true                                {lp64_product}

linux打印jvm参数

java -XX:+PrintFlagsFinal -version |grepUseCompressedOops

Zero Based Compressd Oops

零基压缩优化:针对压缩&解压动作的进一步优化
Zero Based Compressed Oops

指针压缩基本原理

1:通过对齐和偏移量将64位指针压缩成32位
2:零基压缩是针对压缩解压动作的进一步优化,他通过改变正常指针的随机地址分配特性,强制堆地址从零开始分配

启动零基压缩的前提:
分配给JVM的大小必须>4G&&<=32G
1:<4G,直接使用低虚拟地址空间(64位模拟32位),这样就不需要做压缩解压动作了
2:>4G&&<=32G,零基压缩
3:<32G,普通的对象指针压缩技术

实践

VM.current().details()

# Running 64-bit HotSpot VM.
# Using compressed oop with 0-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

开启压缩指针:-XX:+UseCompressedOops

ClassLayout.parseClass(User.class).toPrintable()

com.dashuai.demo.service.order.entity.User object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0    12                    (object header)                           N/A
     12     4                int User.age                                  N/A
     16     4     java.lang.Long User.id                                   N/A
     20     4   java.lang.String User.name                                 N/A
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

关闭压缩指针:-XX:-UseCompressedOops

ClassLayout.parseClass(User.class).toPrintable()

com.dashuai.demo.service.order.entity.User object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0    16                    (object header)                           N/A
     16     4                int User.age                                  N/A
     20     4                    (alignment/padding gap)                  
     24     8     java.lang.Long User.id                                   N/A
     32     8   java.lang.String User.name                                 N/A
Instance size: 40 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

header占用16个字节,之后的4个字节是对象的int属性,之后又4个字节的对齐填充。
由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,也就是说就是对象的大小必须是8字节的整数倍。
如上header加上fileld是36字节,所以需要4字节的对齐填充。

总结

  1. 优点:增加了Heap的大小
  2. 缺点:解压缩对于JVM来说性能有一定损耗
  3. 涉及知识点:ES性能优化(ES单个节点最大内存配置机制,此处具体不拓展)
  4. 大量对象的情况下,若想充分利用性能,设计类时需要细节考虑计算每个实体类所占jvm内存大小,将空间充分利用

引用
https://juejin.im/post/6844903873346453518#heading-14

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐