Java的自動內存管理機制(automatic storage management system known as a garbage collector)省卻了不少編碼工做,大大地提升了Java的生產力,並且JVM的性能也愈來愈好,特別是G1的出現,改善了垃圾回收中stop the world的情況。html
也許不少人都沒有考慮過這個問題,new一個Object對象到底佔用多少內存呢( Object obj = new Object() )?java
這裏很明確的是obj是一個指向對象的引用(reference - there are three kinds of reference types: class types,array types, and interface types),引用的長度決定了Java的尋址能力,32位的JDK是4字節,64位的JDK是8字節(指針未被壓縮的狀況下)。web
由於obj對象沒有任何數據(field),會在堆上爲它分配空間嗎?若是分配空間,裏面存儲了什麼內容?數據庫
以面向對象的思惟來分析,對象封裝了數據和行爲,是一個統一的總體,雖然obj對象沒有數據,可是有行爲(Object類定義了12個方法)。當咱們執行完new操做後,obj的值爲堆內存的地址,既然obj都指向一塊內存了,說明是會在堆上爲其分配空間的。編程
那麼分配的空間有多大,存儲了什麼內容呢?在The Java Virtual Machine Specification Java SE 7 Edition和The Java Language Specification Java SE 7 Edition裏面沒有找到相關的描述,這極可能是屬於JVM實現自由控制的範疇了。咱們能夠利用JDK自帶的工具jvisualvm.exe來查看分配的空間有多大。爲了方便在jvisualvm中查看對象佔多少內存,這裏使用一個私有的靜態內部類EmptyObject來替代Object,由於類定義爲空,因此能夠等同對待EmptyObject和Object。架構
/** * 構造一個無任何字段的空對象佔多少內存 * @author 楊尚川 */ public class EmptyTest { public static void main(String[] args) throws InterruptedException{ //加到集合中,使垃圾沒法回收 List<EmptyObject> emptys = new ArrayList<>(); for(int i=0;i<100;i++){ emptys.add(new EmptyObject()); } //打開jvisualvm,查看EmptyObject的大小爲16字節 Thread.sleep(60*1000); } private static class EmptyObject{} }
咱們在這裏面經過new不一樣的對象數(for循環次數),來分析內存佔用,new 1個對象是16字節,new 2個對象是32字節,new 100個對象是1600字節,經過不少次的嘗試,咱們從jvisualvm裏面能夠看到 字節數=對象數*16 ,咱們有理由相信對象數跟字節數的線性關係。從這裏能夠看出,jvisualvm顯示的內存佔用跟引用的4字節或8字節是沒有關係的,也就是說,jvisualvm顯示的是堆內存佔用,這也很好理解,畢竟全部引用的字節佔用是固定的。8字節是引用,16字節是堆內存,總共是8+16=24字節,因此new一個Object對象佔用8+16=24字節(64位JDK)。oracle
若是JDK是32位,按如上分析方法可知new一個Object對象佔用4+8=12字節(32位JDK),以下圖所示:ide
64位JDK:
32位JDK:工具
那麼分配的16字節(8字節)的堆內存中存儲了什麼內容呢?當咱們Object obj = new Object();的時候,在棧內存中有一個引用obj,他多是32位也多是64位,obj實質只是一個內存地址,那麼當咱們調用obj.xxx()的時候,JVM怎麼知道obj是哪一個類的實例呢?因此,能夠大膽地推測,obj對象的16字節(8字節)的堆內存中記錄了對象屬於哪一個類的信息,問題是這16字節(8字節)的結構是什麼樣的?不清楚!佈局
不過咱們仍然能夠大膽地猜想一下,經過上面的64位和32位的堆內存大小對比分析發現,堆內存分配的大小是引用的兩倍,上面咱們已經猜想堆內存中會記錄對象是哪一個類的實例,如何記錄呢?由於類對象是放置在方法區的,類對象自己也是一個對象,所以能夠經過一個引用指向它,因此堆內存有可能就是放置了兩個引用,指向兩個對象。分析到這裏,事情就比較明朗了,堆內存中可能就放置了兩個內存地址,一個指向EmptyObject.class(在實驗代碼中用EmptyObject來代替Object),一個指向什麼呢?不清楚!
在Inside the Java 2 Virtual Machine 2nd by Bill Venners的5.3.5中有這麼一段描述:
The Java virtual machine specification is silent on how objects should be represented on the heap. Object representation--an integral aspect of the overall design of the heap and garbage collector--is a decision of implementation designers.
好了,事情最終清楚了,JVM規範並無規定Java對象在堆中是如何表示的,對象的表示是堆和垃圾收集器的總體設計的一個組成部分,這是由JVM實現的設計師來決定的。 所以,若是咱們真的想搞清楚對象是如何表示的,那麼須要查詢HotSpot VM或是其餘JVM實現的相關資料。
在淘寶工程師莫樞(撒迦)的《JVM分享》PPT的第112頁介紹了「HotSpot中的Java對象佈局」,這真是如今關心的內容,經過PPT的介紹說明前面的猜想是對的,以下圖所示:
咱們研究new一個Object對象佔多少內存可能沒什麼實際意義,由於咱們在編程的時候就能夠肯定對象樹,基本能夠肯定對象大小,除了變長字段,固然,變長字段咱們通常也會有長度限制。因此咱們真正關心的是全部數據最終的大小,也就是數據庫的大小。
面向對象的分析、設計和編程都把「封裝」奉爲圭臬,「分層」更是架構設計中相當重要的設計準則。由於只有這樣,才能實現基本的解耦、讓協做分工成爲可能,知足工業要求的最大化生產力的最終目標。
那麼這種沒有什麼實際意義的問題爲何要研究呢?我以爲只能用三個字來形容:好奇心。好奇心是驅使咱們研究技術的強大推力,當咱們工做了不少年,尤爲是在不重視技術的公司,咱們對技術還有激情嗎?保持一顆敏感好奇的心,也許技術之路能夠走的更長更遠。
這篇文章的重點是展現一種分析問題的思路,要大膽猜想,當心求證,追本溯源,引經據典。求證方式:查找標準規範、查找經典權威書籍、本身作實驗、查找源代碼等。
參考資料:
一、Java™ Virtual Machine Technology
二、The Java Virtual Machine Specification Java SE 7 Edition
三、The Java Language Specification Java SE 7 Edition
四、Inside the Java 2 Virtual Machine 2nd by Bill Venners
五、JVM分享