一.運行時的數據區(qū)域劃分
java虛擬機在運行的時候众弓,會將內存分配出這么幾個區(qū)域
程序計數器,虛擬機棧,本地方法棧披坏,方法區(qū)和堆
1.程序計數器:
? ? Program Counter Register是一塊較小的內存空間,它的作用可以看做是當前線程所執(zhí)行的字節(jié)碼的行號指示器盐数。各個線程獨立棒拂,并且是java虛擬機中唯一一個不會發(fā)生OutOfMemoryError情況的區(qū)域。
2.虛擬機棧:
? 虛擬機棧也是線程私有的玫氢。在每個方法在執(zhí)行的時候會創(chuàng)建一個棧幀帚屉,(Stack Frame),用于存放局部變量表漾峡,操作棧攻旦,動態(tài)鏈接,方法出口等信息生逸。每個方法在調用直到執(zhí)行完畢的過程中牢屋,都對應一個棧幀在虛擬機中從入棧到出棧的過程。
如果請求棧的深度大于虛擬機允許的棧深度槽袄,會拋出StackOverflowError伟阔。
如果虛擬機可以動態(tài)擴展,在擴展的時候無法申請到足夠的內存掰伸,會拋出OutOfMemoryError皱炉。
3.本地方法棧:
Native Method Stacks與虛擬機棧所發(fā)揮的作用是非常相似的,只不過一個是執(zhí)行Java方法狮鸭,一個是Nataive方法合搅,HotSpot虛擬機直接將兩者合二為一了。
4.堆:
堆是被所有線程共享的一塊內存區(qū)域歧蕉,在虛擬機啟動的時候創(chuàng)建灾部。
堆是垃圾收集器管理的主要區(qū)域,通常稱為GC堆惯退。
如果在堆中沒有完成對實例的內存分配赌髓,并且其也不再可以進行擴展的時候,會拋出OutOfMemoryError。
5.方法區(qū):
Method Area也是被所有線程共享的一塊內存區(qū)域锁蠕,用于儲存已經被虛擬機加載出的類信息夷野,常量,靜態(tài)變量荣倾,JIT編譯后的代碼等數據悯搔。
同樣在無法申請更多的內存時拋出OutOfMemoryError。
6.直接內存:
Direct Memory并不是虛擬機運行時數據區(qū)的一部分舌仍,也不是Java虛擬機規(guī)范中定義的內存區(qū)域妒貌,但是這部分也是頻繁使用。主要在java中的NIO中使用铸豁,對進行io處理的時候灌曙,會申請直接內存,提高性能节芥。這個區(qū)域不能被忽視平匈,如果個內存區(qū)域之和大于物理內存的限制時,在動態(tài)擴展時也會拋出OutOfMemoryError藏古。
7.運行時常量池:
Runtime Constant Pool是方法區(qū)的一部分增炭。用于存放編譯器生成的各種字面量和符號引用,這部分內容將在類加載后存放到方法區(qū)的運行時常量池中拧晕。
當常量池無法再申請到內存時會拋出OutOfMemoryError異常隙姿。
二.對象
1.對象的創(chuàng)建:
當虛擬機在遇到new指令的時候,會去常量池中尋找是否有對應的這個類的符號引用厂捞,并且檢查是否被加載输玷,解析和初始化過。如果沒有就需要執(zhí)行這些動作靡馁。
java在對對象進行內存分配時欲鹏,會把堆分成已使用和未使用的兩個區(qū)域,如果絕對規(guī)整臭墨,那么中間放一個指針代表臨界點赔嚎。分配的時候只需要移動這個指針與對象相同大小的距離,這個方法叫做指針碰撞胧弛。如果這個堆的區(qū)域不是絕對規(guī)整的尤误,那么就無法使用指針碰撞規(guī)則。這個時候就需要維護一張列表來儲存那些內存塊現在時可用的结缚,這個方式叫做空閑列表损晤。具體使用哪種方法是由所采用的垃圾收集器規(guī)定的。
當然指針碰撞是單線程的红竭,不能在并發(fā)的時候工作尤勋,這個時候就要用到CAS算法喘落,同時在每一個線程中分配一個小內存,用來作為分配緩沖最冰。這種方式叫Thread Local Allocation Buffer(TLAB)瘦棋。
2.對象的訪問定位:
句柄訪問,就是java堆中分配一塊內存區(qū)域作為句柄池锌奴,而reference中儲存的就是對象的句柄地址兽狭,句柄中包含了對象的實例信息和類型的地址信息憾股。
直接指針訪問鹿蜀,reference中存放的是實例的直接訪問地址,需要考慮如何存放類型數據的相關信息服球。
3.實戰(zhàn)OutOfMemory異常:
堆溢出:
運行時異常:
這里使用intern()方法茴恰,這個方法是一個Native方法,作用是在字符串常量池中如果沒有同樣的字符串則添加斩熊,否則直接使用往枣。這里會把字符串常量池塞滿導致常量池發(fā)生OOM。
方法區(qū)溢出:
方法區(qū)用于存放Class的相關信息粉渠,如類名分冈、訪問修飾符、常量池霸株、字段描述符雕沉、方法描述等。對于這個區(qū)域的測試去件,基本的思路是運行時產生大量的類去填滿方法區(qū)坡椒,直到溢出。比如動態(tài)代理會生成動態(tài)類尤溜。
使用CGLib技術直接操作字節(jié)碼運行倔叼,生成大量的動態(tài)類。當前很多主流框架如Spring和Hibernate對類進行增強都會使用CGLib這類字節(jié)碼技術宫莱,增強的類越多丈攒,就需要越大的方法區(qū)來保證動態(tài)生成的Class可以加載入內存。
本機直接內存溢出:
這是由于使用了手動申請直接內存的時候發(fā)生的意外??