在JVM的管控下鹉戚,Java程序員不再需要管理內(nèi)存的分配與釋放,這和在C和C++的世界是完全不一樣的咙冗。所以,在JVM的幫助下漂彤,Java程序員很少會關(guān)注內(nèi)存泄露和內(nèi)存溢出的問題雾消。但是,一旦JVM發(fā)生這些情況的時候挫望,如果你不清楚JVM內(nèi)存的內(nèi)存管理機制是很難定位與解決問題的立润。
JVM內(nèi)存區(qū)域介紹
首先需要弄明白JVM的內(nèi)存區(qū)域,JVM的內(nèi)存區(qū)域如圖:
下面分別對各個區(qū)域的功能做個說明:
程序計數(shù)器:是一塊較小的內(nèi)存空間媳板,可以看做是當前線程所執(zhí)行的字節(jié)碼的行號指示器桑腮。由于Java虛擬機的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻蛉幸,一個核心破讨,或者一個處理器只會處理一個線程的某條指令。因此奕纫,線程切換后能恢復(fù)到正確的執(zhí)行位置提陶,每條線程都需要有一個獨立的程序計數(shù)器,各條線程之間計數(shù)器互不影響匹层,獨立存儲隙笆。所以這個地方也不會出現(xiàn)OutOfMemoryError情況的區(qū)域。
Java虛擬機棧:當每次調(diào)用一個方法的時候升筏,其實就是在JVM內(nèi)存上分配一個棧幀撑柔。這個棧幀包含一些局部變量表等信息。其中局部變量表所需的內(nèi)存空間在編譯期間就完成了分配您访。那針對這個區(qū)域規(guī)定了兩種異常信息:第一種乏冀,如果棧請求的深度大于虛擬機所允許的深度,將拋出StackOverflowError 異常洋只。第二種辆沦,如果虛擬機棧可以動態(tài)擴展识虚,如果擴展時無法申請到足夠的內(nèi)存肢扯,就會拋出OutOfMemoryError異常。
本地方法棧:主要為虛擬機使用到的native 方法服務(wù)担锤。這部分在JVM的規(guī)范里面沒有強制規(guī)定如何實現(xiàn)蔚晨。與虛擬機棧一樣,同樣會拋出StackOverflowError和OutOfMemoryError 兩種異常信息。
Java堆:是被所有線程共享的一塊內(nèi)存區(qū)域铭腕,該內(nèi)存區(qū)域的唯一目的就是存放對象實例银择,幾乎所有的對象實例都是在這里分配創(chuàng)建。由于他是虛擬機中管理的最大一塊內(nèi)存累舷,所以是主要的收集區(qū)域浩考。如果還需要再堆上分配實例,但是無法擴展出足夠的內(nèi)存空間被盈,將會拋出OutOfMemoryError異常析孽。
方法區(qū):也是各個線程共享的部分。主要用于存儲已被虛擬機加載的類信息只怎、常量袜瞬、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)身堡。人們更愿意把這個部分稱為“永久代”邓尤。這個區(qū)域的內(nèi)存回收目標主要是針對常量池的回收和對類型的卸載,一般來說贴谎,這個區(qū)域的回收“成績”比較難以令人滿意汞扎。
內(nèi)存溢出的示例
通過分析內(nèi)存溢出,可以根據(jù)異常信息快速判斷是哪個區(qū)域的內(nèi)存溢出赴精,知道什么樣的代碼可能會導(dǎo)致這些區(qū)域內(nèi)存溢出,以及出現(xiàn)這些異常后該如何處理绞幌。(首先我們一般都是基于Sun公司的蕾哟,HotSpot虛擬機運行的。)
Java堆內(nèi)存溢出
如何產(chǎn)生Java堆內(nèi)存溢出:
只要在Java堆上不斷的創(chuàng)建對象莲蜘,并且保證GC Roots到對象之間存在可達的路徑來避免垃圾回收機制清楚這些對象谭确,那么在對象數(shù)量達到最大堆的容量限制后就會產(chǎn)生內(nèi)存溢出的異常。
Java堆內(nèi)存的大小設(shè)置是通過:-Xms -Xmx 兩個參數(shù)設(shè)置的票渠,-Xms是代表堆的初始化大小逐哈,-Xmx是代表堆的擴展大小。通過參數(shù)-XX: +HeapDumpOnOutOfMemoryError 可以讓虛擬機在出現(xiàn)內(nèi)存溢出異常時Dump出當前的內(nèi)存堆轉(zhuǎn)存儲快照以便事后進行分析问顷。
例如下面這段代碼就會Java堆內(nèi)存溢出:
這里運行時的命令是: java -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError昂秃,限制堆的初始化大小和可擴展到的大小都是20M,并且當發(fā)生內(nèi)存溢出后將內(nèi)存堆快照保存為堆轉(zhuǎn)儲文件杜窄。
當出現(xiàn)Java堆內(nèi)存溢出時肠骆,異常堆棧信息”java.lang.OutOfMemoryError”會跟著進一步提示”Java heap space”.要解決Java堆上的內(nèi)存溢出,需要通過工具進行詳細的排查塞耕,這里先不介紹蚀腿。
虛擬機棧和本地方法棧溢出
虛擬機棧的棧容量是通過-Xss 參數(shù)設(shè)定的。關(guān)于虛擬機棧會存在兩種異常:(但是一般都是拋出StackOverflowError.)
1. 如果線程請求的棧深度大于虛擬機所允許的最大深度扫外,將拋出StackOverflowError異常莉钙。
2. 如果虛擬機在擴展棧時無法申請到足夠的內(nèi)存空間廓脆,則拋出OutOfMemoryError異常。
下面這段代碼就會造成StackOverflowError異常:
這里運行時的命令是:java -Xss160K TestStack停忿,限制棧的空間大小為160k。
方法區(qū)內(nèi)存溢出
常量池是屬于方法區(qū)的一部分蜀涨,為了驗證方法區(qū)內(nèi)存溢出瞎嬉,這里程序就不斷添加常量來造成方法區(qū)的內(nèi)存溢出。首先限制一下方法區(qū)的大泻窳: 我們可以通過-XX:PermSize 和 -XX:MaxPermSize限制方法區(qū)大小氧枣。
產(chǎn)生異常的示例代碼如下:
這里運行時的命令是:java -XX:PermSize=2m -XX:MaxPermSize=2m ConstantPool
從運行結(jié)果中可以看到,運行時常量池溢出别垮,在OutOfMemoryError后面跟隨的提示信息是”PermGen space”便监。
總結(jié)
JVM內(nèi)存區(qū)域劃分,便于它能夠更加高效的管理自身的內(nèi)存碳想。當程序中出現(xiàn)這種由于JVM造成的內(nèi)存溢出的情況的時候烧董,需要根據(jù)不同的情況做不同的分析與處理。
JVM系列還會繼續(xù)往下寫胧奔,持續(xù)更新中逊移,不妨點個贊,*^_^*