当前位置:首页 > 内存 > 正文

java占用内存多大(java占用内存过高)

  • 内存
  • 2024-08-27 01:17:33
  • 3504

一、一个Java对象到底占多大内存对象头
对象头在32位系统上占用8个字节,在64位系统上占用16个字节。
实例数据
原始类型(primitivetype)的内存使用情况如下:
PrimitiveTypeMemoryRequired(bytes)
boolean1
byte1
Short2
char2
int4
float4
long8
double8
引用类型在32位系统上每个占用4个字节,在64位系统上每个引用类型占用8个字节。
对齐padding
HotSpot的对齐是8字节对齐:
(对象头+实例数据+padding)%8等于0且0<=padding<8
指针压缩
明年大象占用的内存大小受VM参数UseCompressedOops影响。
1)对对象头的影响
开启(-XX:+UseCompressedOops)对象头的大小为12字节(64位机)。
staticclassA{
inta;
}
对象A占用的内存:
关闭指针压缩:16+4=20不是的倍数8,所以+padding/4=24
启用指针压缩:12+4=16已经是8的倍数,不需要padding。
1)对引用类型的影响
在64位机器上,引用类型占用8个字节,开启指针压缩后,占用4个字节。
staticclassB2{
intb2a;
Integerb2b;
}
B2对象占用的内存:
关闭指针压缩:16+4+8=28不是8的倍数,所以+padding/4=32
开启指针压缩:12+4+4=20不是8的倍数,所以+padding/4=24
数组对象
在64位机器上,数组对象的对象头占用24字节,开启压缩后占用16字节。它比常规对象占用更多内存的原因是需要额外的空间来存储数组的长度。
首先考虑newInteger[0]占用的内存大小。长度为0,也就是对象头的大小:
不压缩:24bytes
启用压缩:16bytes
那么就很容易计算了newinteger[1]、newInteger[2]、newInteger[3]和newInteger:
未打开压缩:
已启用压缩:
TakenewInteger[3]详细解释一下:
未开启压缩:24(对象头)+8*3=48,不需要padding;
开启压缩:16(对象头)+3*4=28,+padding/4=32,此类推。
这同样适用于自定义类的数组,例如:
staticclassB3{
inta;
Integerb;
}
newB3[3]占用内存大小:
未压缩:48
开启压缩后:32
复合对象
计算复合对象占用内存大小其实就是利用上面的规则,这只是一个问题。
1)对象本身的大小
直接计算当前对象占用的空间,包括当前类和超类的基类型实例的字段大小、实例字段的引用大小引用类型,实例的基类型-数组占用的总空间,实例引用类型数组引用占用的空间大小,但是不包括从超类继承的对象本身以及声明的实例引用字段的大小经过;当前类,以及实例引用数组引用的实际对象的大小。
staticclassB{
inta;
intb;
}
staticclassC{
intba;
B[]as=newB[3];
C(){
for(inti=0;i<;i++){
as[i]=newB();
}
}
}
未开启压缩:16(对象头)+4(ba)+8(作为参考的大小)+padding/4=32
压缩开启:12+4+4+padding/4=24
2)当前对象占用的总空间
递归计算当前对象占用的总空间,包括出现字段大小和当前类和超类的出现次数Field指的是对象大小。
递归计算复合对象占用的内存时,需要注意:对齐填充是以每个对象为单位进行的。看下图就很容易理解了。
现在我们手动计算一下C对象占用的总内存,主要由三部分组成:C对象本身的大小+数组对象的大小+B对象的大小。
禁用压缩:
(16+4+8+4(padding))+(24+8*3)+(16+8)*3=152bytes
启用压缩:
(12+4+4+4(填充))+(16+4*3+4(矩阵对象填充))+(12+8+4(B对象填充))*3=128字节
有兴趣的可以尝试一下。
在实际工作中,真正需要手动计算对象大小的场景很少,但我个人觉得每个Java开发者都应该将其理解为基础知识。另外:你写的代码有多少内存记录了内存里的内容你应该对如何设置有一个直观的认识吧?