最近在看深入Java虛擬機(jī),因為這本書理論為主闸与,我之前看過前幾章毙替,沒多久還是全忘了,為此這遍我想做點(diǎn)筆記践樱,哪怕又忘了厂画,也可以快速拾起來。
Java內(nèi)存區(qū)域和內(nèi)存溢出異常
內(nèi)存區(qū)域介紹
首先拷邢,來看看內(nèi)存區(qū)域袱院。java運(yùn)行時的內(nèi)存區(qū)域主要分為:程序計數(shù)器,堆區(qū)瞭稼,棧區(qū)忽洛,方法區(qū)。
- 程序計數(shù)器
主要保存字節(jié)碼指令环肘,分支欲虚,循環(huán),跳轉(zhuǎn)悔雹,異常處理复哆,線程恢復(fù)等基礎(chǔ)功能。 - 棧區(qū)
主要保存的是基本數(shù)據(jù)類型荠商,以及對象的引用寂恬。在這塊內(nèi)存區(qū)域會拋出兩種異常:StackOverFlowError 這表示棧的深度不夠;OutOfMemoryError 這表示無法申請到足夠的內(nèi)存空間了 - 堆區(qū)
這塊區(qū)域是Java虛擬機(jī)管理的最大的一塊區(qū)域莱没,這塊區(qū)域也是所有線程共享的區(qū)域初肉,這塊區(qū)域主要保存的Java對象,當(dāng)這塊內(nèi)存區(qū)域不夠的時候也會拋出OutOfMemoryError饰躲。這塊區(qū)域的詳細(xì)情況我們下面在介紹牙咏。 - 方法區(qū)(永久代)
這塊區(qū)域主要保存的是常量,靜態(tài)變量嘹裂,類信息等妄壶,這塊區(qū)域也是共享的,當(dāng)這塊區(qū)域內(nèi)存不足的時候也會拋出OutOfMemoryError寄狼。
對象的創(chuàng)建
我們來分析一下Java中new一個對象的過程到底什么樣的呢丁寄?
首先氨淌,這條指令會去做一個check,檢查一個常量池是否可以定位到一個類的符號的引用伊磺;接下來就是要在對內(nèi)存創(chuàng)建一塊內(nèi)存空間了盛正,分配內(nèi)存空間有兩種方式: 指針碰撞方式和空閑列表。
- 指針碰撞
首先屑埋,假設(shè)Java堆內(nèi)存是絕對的規(guī)整豪筝,使用過的內(nèi)存放一邊,未使用過的內(nèi)存放另外一邊摘能,中間放著一個指針作為分界點(diǎn)指示器续崖。 - 空閑列表
如果內(nèi)存不是規(guī)整的就沒辦法進(jìn)行指針碰撞,虛擬機(jī)就必須維護(hù)一個空閑列表团搞,那些是可用的那些是不可用的是通過這個空閑列表來記錄的严望,因此目前大多數(shù)都使用空閑列表。如何劃分內(nèi)存空間也是一個很大的問題莺丑,因為這塊區(qū)域是共享的著蟹,對象的分配是非常頻繁的,在并發(fā)的情況下線程又不是安全的梢莽,如何保證內(nèi)存分配不出問題呢萧豆?第一種:可以使用cas的方式,保證原子性昏名;第二種:可以使用本地線程分配緩沖器(TLAB),也就是說每個線程在Java堆區(qū)域里預(yù)先分配一塊內(nèi)存涮雷,只有這塊預(yù)先分配的內(nèi)存使用完了才會使用同步操作。
對象的內(nèi)存布局
對象在內(nèi)存布局可以分為三塊:對象頭轻局,實例數(shù)據(jù)洪鸭,對象填充。
- 對象頭
第一部分?jǐn)?shù)據(jù)為:哈希碼仑扑,GC分代信息览爵,線程所持有的鎖,偏向線程ID镇饮,偏向時間戳蜓竹。
第二部分?jǐn)?shù)據(jù)為: 類型指針,對象指向它的類元數(shù)據(jù)的指針储藐。 - 實例數(shù)據(jù)
對象存儲的真正有效信息俱济。 - 對象填充
沒有什么含義,起到站位的作用钙勃。
對象的訪問
對象的訪問主流的方式是使用句柄和直接指針
- 句柄
Java堆中將劃分出一塊存儲句柄蛛碌,訪問的時候先從Java棧到句柄池再到具體的對象。 - 直接指針
棧中保存的就是對象的引用辖源,可以通過引用直接訪問到具體的對象蔚携。
這兩種方式比較的話:直接指針更有優(yōu)勢希太,因為它會少移動一次指針。