java boolean占用內存大小說明
答案: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占用內存大小要進行具體分析,不過思路都是一致的。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Java基本數據類型族譜與易錯點梳理解析
- Java基本數據類型之間的相互轉換詳解
- Java中int和Integer的區別
- Java八種基本變量作為類的成員變量的默認值操作
- java數據類型和運算符的深入講解