寫在前面: 虛擬機自動內存管理的機制下,不需要爲每個new操作取寫配對的delete/free代碼,也不易出現內存泄漏和內存溢出問題。也正是因爲把控制內存的權力交給了java虛擬機,一旦出現內存泄漏,後果很嚴重。因此需要了解虛擬機是怎樣使用內存的。
Java虛擬機內存
運行時數據區
-
程序計數器(線程私有)
-
一塊較小的內存空間,可以看做是當前線程執行的字節碼行號的指示器。它是程序控制流的指示器,分支、循環、跳轉、異常處理、線程恢復都依賴計數器來完成。
爲什麼是線程私有? java多線程的虛擬機是通過線程輪流切換、分配處理器執行時間的方式實現,因此,在切換線程時需要恢復正確的位置,所以每個線程都有一個獨立的程序計數器,線程之間計數器互不影響。
執行java方法,計數器記錄的是正在執行的虛擬機字節碼指令的地址;
執行本地方法,計數器爲undefined。
沒有任何OutOfMemoryError的區域
生命週期與線程相同 -
虛擬機棧(線程私有)
生命週期與線程相同,每個方法創建時都會創建一個棧幀用於存儲局部變量表、操作數棧、動態連接、方法出口等信息。
局部變量存放編譯期可知的各種java虛擬機基本數據類型。
線程請求的棧深度大於虛擬機所允許的深度,拋Stack Overflow Error
棧擴展時無法申請到足夠內存會拋出out of memoryError -
本地方法棧(線程私有)
本地方法棧與虛擬機棧類似,唯一不同的是兩者執行的服務不同,前者爲虛擬機使用的本地方法服務,而後者是爲虛擬機執行Java方法服務。
有的java虛擬機會把本地方法棧和虛擬機棧合二爲一。
在棧深度溢出或擴展失敗時分別拋出Stack Overflow Error 或out of memoryError -
堆
虛擬機管理內存中最大的一塊,java堆是被所有線程共享的一塊內存區域,虛擬機啓動時創建。
用途:存放對象,幾乎 所有對象都在這裏分配內存。隨着java逃逸分析技術 的發展,有的對象也可能在棧中分配。
java堆是垃圾收集器管理的內存區域。也叫“GC”堆,java堆中沒有完成內存實例分配且堆無法擴展時拋出OutOfMemoryError.
逃逸分析技術:此處未作詳細說明,感興趣請參考如下這篇文章:https://blog.csdn.net/w372426096/article/details/80938788
-
方法區
線程共享。
用途:用來存儲已被類加載的類型信息、常量、靜態變量、即時編譯器編譯後的代碼緩存等數據。
別名 非堆,與java堆區分。
一個變動:JDK7的HotSpot已經把原本放在永久代的字符串常量池、靜態變量等移出。到 JDK8完全廢棄了永久代的概念,改用元空間 代替
方法區無法滿足新的內存需求分配時拋出OutOfMemoryError- 運行時常量池
方法區的一部分。用於存放編譯期生成的各種字面量與符號引用,在類加載後放到方法去的運行時常量池中。
- 運行時常量池
-
其他
直接內存:並不是虛擬機運行時的一部分
NIO:基於通道緩衝區的I/O方式,使用Native函數庫直接分配堆外內存。