JAVA內存區域中不同的結構會由於不同的原因而導致內存溢出。JAVA內存主要分爲堆,棧,方法區和程序計數器四個部分。程序計數器是唯一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。其他三個區域都有可能發生內存溢出。下面我們來具體說說。
對於內存溢出(Memory Overflow),還有一個相似的概念就是內存泄露(Memory Leak)。它們有着本質的不同,內存泄露會導致內存溢出。內存泄露是沒有必要存活的對象應該被GC回收,然而還有引用指向對象導致GC不能回收,這一般是程序的問題。
一 堆溢出
堆用來存儲對象實例,只要不斷的創建對象,並且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,則在對象數量到達最大堆的容量限制後會產生內存溢出異常。內存溢出在堆中就會導致OOM。下面的例子在需要設定虛擬機的參數:-Xmx(堆的最大值)和 -Xms(堆的初始值)。下圖顯示異常位置:Java heap space
二 棧溢出
棧溢出有兩種異常:
1.如果線程請求的棧深度大於虛擬機所允許的最大深度,則將拋出StackOverflowError異常。
2.如果虛擬機在擴展棧時無法申請到做夠的內存空間,就會拋出OutOfMemoryError異常。
上面兩種情況存在着相互重疊,本質是對同一件事情的兩種描述而已。可以創建很多個棧幀來讓它溢出,其實就是定義一個遞歸方法,讓棧幀佔滿棧空間,這會導致StackOverflowError異常,棧深度一般在1000到2000。
創建很多個線程可以使虛擬機拋出OutOfMemoryError,由於Java線程是映射到操作系統的內核上,因此在做這個實驗時很容易導致死機,我試驗過一次,電腦就卡死了。下面就展示拋出的第一個異常: