深入理解java虛擬機器(一)java虛擬機器的記憶體區域
一、 java虛擬機器記憶體區域主要有:方法區、堆、虛擬機器棧、本地方方法棧、程式計數器
按照執行緒私有和共有來分:執行緒私有的有--程式計數器,虛擬機器棧,本地方法棧。共有的有--本地方法區,堆
1、程式計數器:主要功能是控制程式位元組碼的指令,分支、迴圈、跳轉、異常處理、執行緒恢復等功能
2、java虛擬機器棧(stack):存放區域性變數(8中基本資料型別、物件引用、地址引用)、運算元棧、動態連結、方法出口等
3、本地方法棧:與java虛擬機器類似,java虛擬機器為位元組碼服務,而他是為本地方法服務,Sun HotSport虛擬機器把這兩者合二為一了
4、java堆(Heap):主要存放物件的例項和陣列
5、方法區:主要存放已被虛擬機器載入的類資訊(Class)、常量、靜態變數、編譯器編譯後的程式碼
6、執行時常量池(方法區的一部分):存放編譯器生成的各種字面量和符號引用
7、直接記憶體:不是執行時記憶體的一部分,特殊場景也會使用
二、java物件分配記憶體
最常見的建立物件的方法是new,當使用new關鍵字建立物件時,虛擬機器首先會檢查這個指令的引數是否在常量池中定位到一個類的符號引用,檢查這個符號引用代表的類是否已經被載入、解析和初始化。
檢查聽過後給物件分配記憶體,分配的方式有兩種:
1、假設堆中的物件都是排列緊密的,記憶體放在指標的一邊,另一邊是空閒的,只要移動下指標,繼續存放下一個物件就可以了,這叫做“指標碰撞”
2、當然更可能的時候物件時雜亂的,虛擬機器就維護一個列表,記錄哪塊記憶體區域是可用的,分配的時候找一塊足夠大的空間記憶體給物件,叫做"空閒列表"
可見堆記憶體有可能很整潔也有可能很雜亂,這是由垃圾收集器是否有壓縮整理功能決定的。Serial、ParNew等帶有Compact過程的收集器使用分配演算法是指標碰撞,而CMS
三、最常見的異常
最常見的異常是OutOfMemoryError記憶體溢位異常。
通常配置方法是:-Xms20m -Xmx 100m
虛擬機器棧和本地方法棧同時也有另外一種異常StackOverflowError,兩者區別:
1、執行緒請求的棧深度大於虛擬機器所允許的深度,StackOverflowError
2、虛擬機器在擴充套件時超過所能允許申請的記憶體,OutOfMemoryError
但是這兩種異常有類似重複的地方:當執行緒獲取不到足夠的記憶體時,到底是記憶體太小還是執行緒用的棧太大了?
看如下情況:
1、-Xss改小棧記憶體容量,丟擲StackOverflowError異常,且輸出的堆疊深度相應減小
2、定義了大量的本地變數,增大本地方法棧本地變量表的長度,還是丟擲StackOverflowError異常,且輸出的堆疊深度相應減小
實際的原因是:在單執行緒的情況下,無論是棧幀太大還是記憶體太小都會丟擲StackOverflowError異常
而多執行緒的情況下,如果建立的執行緒太多則會導致記憶體溢位錯誤,即OutOfMemoryError,為什麼呢?
作業系統分配的每個程序的記憶體是有限的,如32位的windows限制為2GB,除去堆和方法區,剩下的程式計數器忽略不計,不算虛擬機器自身消耗,剩下的都被虛擬機器棧和本地方法棧消耗了,每個執行緒分配到的棧容量越大,可以建立的執行緒數就越少,不停的建立執行緒時就容易把剩下的呢村耗盡,就會丟擲記憶體溢位。
改進方法:
1、減少執行緒數
2、換64位虛擬機器
3、減少堆記憶體和本地方法記憶體容量換取更多的執行緒