Java虛拟機-記憶體劃分
==================================
在Java中,在虛拟機自動記憶體機制管理的幫助下,不需要程式員為每一個new操作去寫對應的delete/free代碼,不容易出現記憶體洩漏和記憶體溢出的錯誤,這一切由虛拟機來管理記憶體,即将記憶體的管理權交給了虛拟機,這樣的好處是減輕了程式員的負擔。但是虛拟機并不能完全掌控整個記憶體,在有些情況下還是會出現記憶體溢出和記憶體洩漏的錯誤,此時如果不了解虛拟機的記憶體管理,排除代碼的錯誤的時候就會非常艱難。
運作時資料區域
程式計數器
程式計數器是一塊比較小的記憶體區域,它可以當作是線程所執行的位元組碼的行号訓示器。在虛拟機的概念模型裡,位元組碼訓示器工作時就是通過這個程式計數器來挑選下一條要執行的位元組碼指令。循環、分支、跳轉、異常處理和線程回複等都需要這個計數器來完成。
在Java虛拟機中,多線程是通過線程輪流切換并配置設定處理器執行時間的方式來實作的。任意一個确定的時刻,一個處理器(即一個核心)都隻會執行一條線程中的某個指令,為了線程切換後程式可以恢複到正确的執行位置,每個線程都需要有一個獨立的程式計數器。并且各個線程之間的計數器不互相影響。稱這類記憶體區域為“線程私有”的記憶體。
在Java中,如果程式正在執行的是一個Java方法,則這個程式計數器記錄的是虛拟機位元組碼指令的位址;如果是Native方法,則計數器的值為空,此記憶體區域是惟一一個在Java虛拟機規範中沒有規定OutOfMemoryError的記憶體區域。(Native方法可以了解為Java語言同其他語言的接口如C++/C)
Java虛拟機棧
虛拟機棧即虛拟機棧中局部變量表部分。他也是線程私有的,它的生命周期與線程是同步的。虛拟機棧描述的是Java方法執行的記憶體模型。每個方法在執行的時候都會建立一個棧幀,用于存儲局部變量表、操作數棧、動态連接配接和方法出口等資訊。每一個方法從調用到結束的過程都對應着棧幀從入棧到出棧的過程。
對于我們經常使用的八種基本資料類型(boolean、char、byte、short、int、float、double、long)、對象引用(不同于對象,隻是一個指向對象起始位址的指針或者指向對象相關位址)和returnAddress類型(指向一條位元組碼指令位址),這些類型的變量在編譯階段就會被存儲在虛拟機棧的局部變量表中。其中double和long類型的資料會占用兩個局部變量表空間,其他的類型都隻占用一個。并且對于一個方法的局部變量表空間是在編譯階段就已經配置設定完成。當運作時,進入方法體之後他的局部變量表空間的大小不會再發生變化。
對于此塊區域,在執行時可能出現兩種異常情況:當線程請求的棧深度大于虛拟機所允許的棧深度就會抛出StackOverflowError異常;如果虛拟機棧可以動态擴充,擴充時無法申請到足夠的記憶體,就會抛出OutofMemoryError異常。
本地方法棧
本地方法棧與Java虛拟機棧發揮的作用是一樣的,隻不過本地方法棧是為Native方法服務,虛拟機棧是為Java方法服務。在虛拟機棧規範中對于Native方法使用的具體語言、使用方法和資料結構沒有強制規定,具體的虛拟機可以自由實作,有的虛拟機将這兩個棧合二為一。同樣的本地方法棧也會抛出StackOverflowError異常和OutofMemoryError異常。
Java堆
Java堆是java虛拟機記憶體管理的最大的一塊區域,Java堆是被所有線程共享的一塊記憶體區域,在虛拟機啟動時建立,它主要用來存放對象執行個體,幾乎所有的對象執行個體都在Java堆中配置設定記憶體。在java虛拟機規範中,所有的對象執行個體和數組都是在Java堆中配置設定記憶體。由于Java堆的作用,它也成為了垃圾回收器所管理的主要區域,有時也稱Java堆為GC堆。現在的記憶體回收機制都采用分代回收算法,Java堆可以分為新生代和老年代,在可細分為Eden空間、From Survivor空間和To Survivor空間;從記憶體配置設定的角度劃分,記憶體共享的Java堆可能劃分出多個線程私有的配置設定緩沖區。但是不論如何劃分,鬥魚存放内容無關,不論哪一塊區域存放的都是對象的執行個體。劃分記憶體隻是為了更快的配置設定記憶體或者回收記憶體。在java虛拟機規範中,java堆可以位于實體上不連續的記憶體空間上隻要邏輯上是連續的即可。在實作時可以是固定的大小也可以動态擴充。如果堆中沒有記憶體完成執行個體配置設定或者堆也無法動态擴充時,就會抛出OutofMemoryError異常。
方法區
方法區與Java堆一樣是各個線程共享的區域。他主要用來存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。Java虛拟機規範把方法區描述為堆的一個邏輯部分,他的别名叫“非堆”。Java虛拟機規範對于方法區的限制比較寬松,不需連續的記憶體、可以選擇固定大小或者可擴充、還可選擇不實作垃圾收集器。當方法區無法滿足記憶體配置設定需求時将抛出OutofMemoryError異常。
運作時常量池
運作時常量池是方法區的一部分,class檔案中除了有類的版本、字段、方法、接口資訊的描述外,還有一項是常量池,用于存放編譯時期生成的各種字面量和符号引用,這部分内容将在類加載後進入方法區運作時常量池中存放。運作時常量池相對于class常量池的另一個重要特征是具備動态性。Java語言并不要求常量隻有編譯時才能産生,也就是并非預置入class檔案常量池中的常量才能進入運作時常量池,運作時也可将新的常量放入到常量池中。如String的intern()。運作時常量池是方法區的一部分,自然會受到方法區記憶體大小的限制,當需要的常量池無法申請到時就會抛出OutofMemoryError異常。