答案:4B或1B
详细
1、如果boolean是单独使用:boolean占4个字节。
2、如果boolean是以boolean数组形式使用:boolean占1个字节
解释
1、JVM没有提供boolean类型专用的字节指令,而是使用int相关指令来代替。
2、对boolean数组的访问与修改,会共用byte数组的baload和bastore指令。
分析结论
上面的第一个结论是说:boolean在底层实际调用int,那么既然int占4个字节,boolean页自然占4个字节。即 boolean类型占4个字节。
上面的第2个结论是说:boolean数组在底层会用byte指令,那么既然byte占1个字节,boolean数组中的boolean也就占1个字节,即,boolean数组中的boolean占1个字节。
扩展
1、因此,大多数对于boolean,byte,char和short类型数据的操作,实际都提升int,并使用int做为运算类型,所以他们占4个字节,实际上,虚拟机规范也只有4字节和8字节类型(long,float),boolean,char,short都是占了4字节。
2、对于在栈上(局部变量)的byte,char,short类型的数据,在内存中的确会占4字节,但这对于(数组)对象来说并不适用。
java各种类型对象占用内存情况分析
经典篇,有图有真相
为什么写这篇文章?
其实一般的程序猿根本不用了解这么深,只有当你到了一定层次,需要了解jvm内部运行机制,或者高并发多线程下,你写的代码对内存有影响,你想做性能优化。。。等等等等,一句话,当你想深入了解java对象在内存中,如何存储,或者每个对象占用多大空间时,你会感谢这篇文章
本文主要分析jvm中的情况,实验环境为64位window10系统、JDK1.8,使用JProfiler进行结论验证
很多描述以及 概念是基于你懂基本java知识的,如果你看起来有点吃力,要加油咯
基本数据类型占用
类型 | 占用空间 |
---|---|
boolean、byte | 1byte |
short、char | 2byte |
int、float | 4byte |
long、double | 8byte |
接下来用JProfiler验证:
新建一个空对象,观察空对象内存占用
public class TestObject { }
对象占用内存 16b,如图
结论:一般自建空对象占用内存 16b,16 = 12 + 4
在TestObj中新增一个 int 属性,观察对象内存占用
public class TestObj { private int i; }
对象占用内存 16b,如图
结论:int 占用 4b, 4 = 16 -12
在TestObj中新增一个 long 属性,观察对象内存占用
public class TestObj { private long i; }
对象占用内存 24b,如图
结论:long 占用 8b, 8 = 24 -12 - 4
其余基本类型可以参照以上自行验证,原理一样
包装类型占用
包装类(Boolean/Byte/Short/Character/Integer/Long/Double/Float)占用内存的大小等于对象头大小加上底层基础数据类型的大小。
类型占用空间Boolean、Byte16byteShort、Char16byteInteger、Float16byteLong、Double24byte 在TestObj中新增一个 Integer 属性,观察对象内存占用
类型 | 占用空间 |
---|---|
Boolean、Byte | 16byte |
Short、Char | 16byte |
Integer、Float | 16byte |
Long、Double | 24byte |
public class TestObj { private Integer i =128; }
对象占用内存 32b,如图
结论:Integer 占用 16b, 16 = 32 - 16
特别的:-128~127 之间的封装类型,只占用 4b**
在TestObj中新增一个 Long 属性,观察对象内存占用
public class TestObj { private Long l = new Long(1); }
对象占用内存 40b,如图
结论:Long 占用 24b, 16 = 40 - 16
其余包装类型可以参照以上自行验证,原理一样
基本类型数组占用
64位机器上,数组对象的对象头占用24 bytes,启用压缩后占用16字节。比普通对象占用内存多是因为需要额外的空间存储数组的长度(普通16b-12b)。
对象数组本身的大小=数组对象头 + length * 存放单个元素大小
在TestObj中新增一个 char[] 属性,观察对象内存占用
public class TestObj { private char[] c = {'a','b','c'}; }
对象占用内存 40b,如图
结论:char[3] 占用 24b, 24 = 40 - 16,24 = 16 + 3 * 2 + 2
封装类型数组占用
封装类型数组比基本类型的数组,需要多管理元素的引用
对象数组本身的大小=数组对象头+length 引用指针大小 + length 存放单个元素大小
在TestObj中新增一个 Integer[] 属性,观察对象内存占用
public class TestObj { private Integer[] i = {128,129,130}; }
对象占用内存 80b,如图
结论:Integer[3] 占用 80b, 80 = 96 - 16 , 80 = 16 + 3 4 + 3 16 +4
String占用内存 在TestObj中新增一个空 String 属性,观察对象内存占用
public class TestObj { private String s = new String(""); }
对象占用内存 40b,如图
结论:String 本身占用 24b, 24 = 40 -16,另外,String的属性value还需要 16b,也就是说空””也需要16b
注意:这里为什么要写String s = new String(“”)?请自己思考,不写会怎么样?
答:如果写成String s = “”,是不会再堆中开辟内存的,也就看不到String占用的空间,你看到的将会是下面的,至于为什么,都是因为final
ArrayList, HashMap的内存占用
这些参考文章开头提到的那篇文章,下面给出计算公式:
一个ArrayList实例本身的的大小为:
12(header) + 4(modCount) + 4(size) + 4(elementData reference) = 24 (bytes)
下面分析一个只有一个Integer(1)元素的ArrayList实例占用的内存大小。
ArrayList<Integer> testList = Lists.newArrayList(); testList.add(1);
根据上面对ArrayList原理的介绍,当调用add方法时,ArrayList会初始化一个默认大小为10的数组,而数组中
保存的Integer(1)实例大小为16 bytes。
则testList占用的内存大小为:
24(ArrayList itselft) + 16(elementData array header) + 10 * 4(elemetData reference) + 16(Integer) = 96 (bytes)
JProfiler中的结果验证了上述分析:
2. HashMap内存占用
这里分析一个只有一组键值对的HashMap, 结构如下:
Map<Integer, Integer> testMap = Maps.newHashMap(); testMap.put(1, 2);
首先分析HashMap本身的大小。HashMap对象拥有的属性包括:
/** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also tolerate length zero in some operations to allow * bootstrapping mechanics that are currently not needed.) */ transient Node<K,V>[] table; /** * Holds cached entrySet(). Note that AbstractMap fields are used * for keySet() and values(). */ transient Set<Map.Entry<K,V>> entrySet; /** * The number of key-value mappings contained in this map. */ transient int size; /** * The number of times this HashMap has been structurally modified * Structural modifications are those that change the number of mappings in * the HashMap or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the HashMap fail-fast. (See ConcurrentModificationException). */ transient int modCount; /** * The next size value at which to resize (capacity * load factor). * * @serial */ // (The javadoc description is true upon serialization. // Additionally, if the table array has not been allocated, this // field holds the initial array capacity, or zero signifying // DEFAULT_INITIAL_CAPACITY.) int threshold; /** * The load factor for the hash table. * * @serial */ final float loadFactor;
HashMap继承了AbstractMap<K,V>, AbstractMap有两个属性:
transient Set<K> keySet; transient Collection<V> values;
所以一个HashMap对象本身的大小为:
12(header) + 4(table reference) + 4(entrySet reference) + 4(size) + 4(modCount) + 4(threshold) + 8(loadFactor) + 4(keySet reference) + 4(values reference) = 48(bytes)
接着分析testMap实例在总共占用的内存大小。
根据上面对HashMap原理的介绍,可知每对键值对对应一个Node对象。根据上面的Node的数据结构,一个Node对象的大小为:
12(header) + 4(hash reference) + 4(key reference) + 4(value reference)+ 4(next pointer reference) = 28 (padding) -> 32(bytes)
加上Key和Value两个Integer对象,一个Node占用内存总大小为:32 + 2 * 16 = 64(bytes)
JProfiler中结果:
下面分析HashMap的Node数组的大小。
根据上面HashMap的原理可知,在不指定容量大小的情况下,HashMap初始容量为16,所以testMap的Node[]占用的内存大小为:
16(header) + 16 * 4(Node reference) + 64(Node) = 144(bytes)
JProfile结果:
所以,testMap占用的内存总大小为:
48(map itself) + 144(Node[]) = 192(bytes)
JProfile结果:
这里只用一个例子说明如何对HashMap进行占用内存大小的计算,根据HashMap初始化容量的大小,以及扩容的影响,HashMap占用内存大小要进行具体分析,不过思路都是一致的。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持自学编程网。
- 本文固定链接: https://zxbcw.cn/post/214115/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)