在Java虛擬機的幫助下不需要手動爲每個對象分配內存和釋放內存,這樣不容易出現內存泄漏的情況。
在Java虛擬機運行時會管理以下幾個區域
線程間隔離的:虛擬機棧、本地方法棧、程序計數器
線程間共享的:方法區、堆
程序計數器:當前線程執行的字節碼行號指示器。
每條線程均需要一個獨立的程序計數器來記錄當前執行的位置
如果正在執行的是一個Java方法則計數器只想位置是字節碼的地址,如果是Native方法 則計數器值爲空
Java虛擬機棧:線程私有,描述Java方法執行的內存模型。方法從調用到執行完成的過程對應着一個棧幀入棧出棧的過程。每個方法在執行的同時會創建一個棧幀來存儲方法的局部變量表、操作數棧、動態鏈接、方法出口等信息
關鍵在局部變量表:存放着編譯期可知的各種基本數據類型(方法中使用到的數據類型)、對象引用(reference類型可能指向對象起始地址的引用指針或指向代表對象的句柄或其他和此對象相關的位置)、returnAddress類型(指向一條字節碼指令的地址)
局部變量表所需空間在編譯期間完成分配,方法執行時在棧幀中分配多少空間是已知且固定的,運行期間不會改變局部變量表的大小。64位長度的long和dubbo類型數據佔兩個局部變量空間其他類型佔1個 可能出現異常 堆棧溢出和內存不足 StackOverflowError、OutOfMeoryError
當方法遞歸引用過深將有可能會出現堆棧溢出
本地方法棧:線程私有!!! 不要被名字迷惑!!! 爲虛擬機使用到的Native方法服務。 本地方法棧也有可能拋出StackOverflowError和OutOfMemoryError(有的虛擬機將本地方法棧和虛擬機棧合二爲一淚如Sun HotSpot 因爲他們功能作用基本一致 只是服務對象有類別之分)
Java堆:線程共享 虛擬機管理的最大的一塊內存區域。 基本所有的在程序中創建的對象實例和數組都在堆上分配內存
細分爲新生代、老年代 新生代還可分爲一塊Eden區和兩塊Survivor區域(8:1:1)。 GC垃圾收集器也是主要採用分代收集算法回收該塊內存 可以使用-Xmx -Xms設置堆大小
方法區:線程共享 存儲虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼 也可稱之爲永久代(在HotSpot虛擬機上GC分帶手機算法擴展至方法區,其他虛擬機不存在永久代概念。)1.7版本中方法區中的字符串常量池已經從方法區移至堆中
直接內存:JDK1.4加入的NIP類 引入基於通道和緩衝區Buffer的I/O方式 可以使用Native函數庫直接分配堆外內存 通過存儲在Java堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作
對象內存佈局:對象在內存中的佈局分爲三塊:對象頭、實例數據、對其填充
對象頭存放自身運行時數據 :哈希碼、GC分代年齡、鎖狀態標誌、線程持有鎖、偏向線程ID、偏向時間戳 固定長度 32或64位(視操作系統定)
對象實例:對象在程序中真正有意義的部分
對齊填充:保證對象起始地址是八字節的整數倍
對象訪問定位通過虛擬機棧存儲的reference類裏面的對象地址或者對象句柄地址再通過句柄存儲地址訪問堆中對象