JVM的五大內存區域
1、程序計數器
2、方法區(也稱為永久代,后續被Metaspace取代)
3、虛擬機棧
4、本地方法棧
5、堆
1、程序計數器
記錄線程的執行位置,線程私有內存
在多線程的情況下,線程被切換回來的時候能夠知道該線程上次運行到哪兒了
2、方法區
方法區是所有線程共享的內存區域,用於存儲已被虛擬機加載的類信息、常量、靜態變量
放在永久代中經常會出現內存溢出,即PermGen Space異常,所以用元空間取而代之
元空間直接使用本地內存,理論上電腦有多少內存它就可以使用多少內存,所以不會再出現PermGen Space異常
java虛擬機對方法區比較寬松,可以選擇不實現垃圾收集
3、虛擬機棧(stack)
每個線程都有各自的Java虛擬機棧,而且隨着線程的創建而創建,隨着線程的死亡而死亡.
Java虛擬機棧會為每一個即將運行的Java方法創建“棧幀”,用於存儲局部變量表、操作棧、動態連接、方法返回地址等
每個方法從開始調用到執行完成的過程,就是棧幀從入棧到出棧的過程
如果方法A調用了方法B,那么A就會先入棧創建一個棧楨,接着B再入棧成為棧頂,B執行完先出棧,接着A執行完出棧。
Java虛擬機棧可能出現兩種類型的異常:
1、線程請求的棧深度大於虛擬機允許的棧深度,將拋出StackOverflowError。
2、虛擬機棧空間可以動態擴展,當動態擴展是無法申請到足夠的空間時,拋出OutOfMemory異常。
4、本地方法棧
本地方法棧和Java虛擬機棧實現的功能與拋出異常幾乎相同
只不過虛擬機棧是為虛擬機執行Java方法服務,本地方法棧則為虛擬機使用到的本地方法服務.
線程開始調用本地方法時,會進入一個不再受JVM約束的世界
本地方法可以通過JNI(Java Native Interface)來訪問虛擬機運行時的數據區
5、堆(heap)
堆區是所有線程共享的內存區域
堆是OOM故障最主要的發源地,它存儲着幾乎所有的實例對象
通常情況下,它占用的空間是所有內存區域中最大的,但如果無節制地創建大量對象,也容易消耗完所有的空間
堆的內存空間既可以固定大小,也可運行時動態地調整,通過如下參數設定初始值和最大值
-Xms256M. -Xmx1024M
其中,-X表示它是JVM運行參數,ms是memorystart的簡稱 最小堆容量,mx是memory max的簡稱 最大堆容量
但是在通常情況下,服務器在運行過程中,堆空間不斷地擴容與回縮,勢必形成不必要的系統壓力,
所以在線上生產環境中,JVM的Xms和Xmx設置成一樣大小,避免在GC后調整堆大小時帶來的額外壓力
堆分成兩大塊:新生代和老年代(默認空間比例為1:2)
新生代分為三塊:Eden區、From Survivor區和To Survivor區(默認空間比例為8:1:1)
對象產生之初在新生代,步入暮年時進入老年代,但是老年代也接納在新生代無法容納的超大對象
Survivor區每個對象都有一個計數器,每次YGC都會加1,默認加到15次,就要移送到老年代
Eden區 -> Survivor 0區 -> Survivor 1區 -> 老年區的過程
1、所有對象都在伊甸園出生,當伊甸園占滿時,開始進行一次Young GC,此次GC會將已存活的對象復制到S0區中
2、伊甸園區又被占滿時,此時又進行一次Young GC,伊甸園存活的對象又復制到S0區。
3、在若干次Young GC后,幸存區S0也滿了,此時Young GC會對伊甸園和幸存區S0的做一次垃圾回收,將兩個區存活的對象復制到幸存區S1中,再把伊甸園和S0清空,最后把S1的內存與S0交換,此時S1又騰空了,S0剩下一些老對象。
4、又經歷若干次GC,幸存區S0已經放滿了經歷過N次GC都回收不了的老對象,此時會將老對象復制到老年代中,騰空伊甸園和幸存區。
(並非當幸存區被老對象占滿才復制到老年代中,當老對象年齡達到15歲,即經歷過15次GC都還活着的,也會復制到老年代中)
(另外伊甸園中如果誕生了一個比幸存區還大的對象,那么該對象回收不了時,也會直接送入到老年代中)
5、又經歷過若干次GC后,老年代也滿了,那么此時它會進行一次Full GC。
6、當執行Full GC后,老年代還是放不下新來的對象時,就會拋出OOM
堆特點
Java虛擬機所需要管理的內存中最大的一塊,被所有線程共享,在虛擬機啟動時創建
堆是垃圾回收的主要區域,所以也被稱為GC堆.
當線程請求分配內存,但堆已滿,且內存已滿無法再擴展時,就拋出OutOfMemoryError