Java內存結構的幾大部分如下圖:
接下來,會對上面每部分區域的功能一一解釋。
1、程序計數器:是線程私有區,是內存中一塊較小的區域,是當前線程執行的字節碼指令的行號指示器,如果線程執行的是Java方法,程序計數器記錄的是正在執行的虛擬機字節碼指令的地址,如果執行的是native方法,程序計數器存儲的是undefined,此區域是內存中唯一一塊沒有規定任何OutOfMemoryError(內存溢出)情況的區域,為什么?因為我們不需要操作該區域,該區域是內部維護的。
2、虛擬機棧:是線程私有的,該區域所描述的是Java方法執行的動態內存模型,每個方法在執行的時候都會創建一個棧幀,用來存儲局部變量表,操作數棧,動態鏈接,方法出口等信息,局部變量表存放的是編譯期可知的基本數據類型,引用類型,returnAddress等,局部變量表的內存在編譯期完成分配,進入方法后,這個方法需在幀中分配多少內存是固定的,在方法運行期間不會改變局部變量表的大小。如果說棧幀堆滿了整個棧,會出現StackOverflowError(棧溢出)異常,棧也可以申請更大的內存,如果申請不到,會拋出OutOfMemoryError異常。
3、本地方法棧:是為本地的native方法服務的,其他的都和虛擬機棧一樣。
4、堆:線程共享的一塊區域,用來存放對象實例的(由於現在有了逃逸分析技術,也可以將對象分配在棧上),該區域是垃圾回收的主要區域,垃圾回收主要是分代回收,有年輕代和老年代,堆可以是物理上不連續的區域,只要邏輯上連續即可。在堆中分配內存的方法有碰撞指針(前提是區域絕對規整,注意多線程同步問題,可以采用CAS原理加失敗重試實現或者本地線程分配緩沖)和空閑列表(不是規整的內存,就是有一個表記錄空閑的內存,然后分配后,從該表中去除),堆空間不足時會出現OutOfMemoryError異常。
5、方法區:線程共享的區域,存儲JVM加載的類信息(類的版本,字段,方法,接口),常量,靜態變量以及即時編譯后的代碼等數據。方法區還有一塊運行時常量池,class文件中的常量池在類加載后就被放入運行時常量池,運行時常量池相對於class文件的常量池具有動態性,可以在運行期間通過intern將常量放入運行時常量池中,方法區空間不足時會拋出OutOfMemoryError異常。
除了Java虛擬機規定的這幾塊區域以外呢,還存在一個堆外內存,即直接內存,直接內存能減少IO時的內存復制了,實現零拷貝,而且沒有GC,減少了垃圾回收的工作,加快了復制的速度,該區域也難以控制,如果內存泄漏很難排查。