轉載自:http://www.importnew.com/14604.htmlhtml
Java虛擬機規範規定JVM的內存分爲了好幾塊,好比堆,棧,程序計數器,方法區等,而Hotspot jvm的實現中,將堆內存分爲了三部分,新生代,老年代,持久帶,其中持久帶實現了規範中規定的方法區,而內存模型中不一樣的部分都會出現相應的OOM錯誤,接下來咱們就分開來討論一下。java
棧溢出(StackOverflowError)spring
棧溢出拋出java.lang.StackOverflowError
錯誤,出現此種狀況是由於方法運行的時候棧的深度超過了虛擬機允許的最大深度所致。數組
出現這種狀況,通常狀況下是程序錯誤所致的,好比寫了一個死遞歸,就有可能形成此種狀況。 下面咱們經過一段代碼來模擬一下此種狀況的內存溢出。服務器
public class OOMTest{ public void stackOverFlowMethod(){ stackOverFlowMethod(); } public static void main(String... args){ OOMTest oom = new OOMTest(); oom.stackOverFlowMethod(); } }
運行上面的代碼,會拋出以下的異常:多線程
Exception in thread "main" java.lang.StackOverflowError at OOMTest.stackOverFlowMethod(OOMTest.java:6)
堆內存溢出的時候,虛擬機會拋出java.lang.OutOfMemoryError:java heap space
,出現此種狀況的時候,咱們須要根據內存溢出的時候產生的dump文件來具體分析(須要增長-XX:+HeapDumpOnOutOfMemoryError
jvm啓動參數)。出現此種問題的時候有多是內存泄露,也有多是內存溢出了。
若是內存泄露,咱們要找出泄露的對象是怎麼被GC ROOT引用起來,而後經過引用鏈來具體分析泄露的緣由。
若是出現了內存溢出問題,這每每是程序本生須要的內存大於了咱們給虛擬機配置的內存,這種狀況下,咱們能夠採用調大-Xmx來解決這種問題。框架
下面咱們經過以下的代碼來演示一下此種狀況的溢出:dom
1
2
3
4
5
6
7
8
9
10
|
import
java.util.*;
import
java.lang.*;
public
class
OOMTest{
public
static
void
main(String... args){
List<
byte
[]> buffer =
new
ArrayList<
byte
[]>();
buffer.add(
new
byte
[
10
*
1024
*
1024
]);
}
}
|
咱們經過以下的命令運行上面的代碼:jvm
java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest
spa
程序輸入以下的信息:
1
2
3
4
5
|
[GC 1180K->366K(19456K),
0.0037311
secs]
[Full GC 366K->330K(19456K),
0.0098740
secs]
[Full GC 330K->292K(19456K),
0.0090244
secs]
Exception in thread
"main"
java.lang.OutOfMemoryError: Java heap space
at OOMTest.main(OOMTest.java:
7
)
|
從運行結果能夠看出,JVM進行了一次Minor gc和兩次的Major gc,從Major gc的輸出能夠看出,gc之後old區使用率爲134K,而字節數組爲10M,加起來大於了old generation的空間,因此拋出了異常,若是調整-Xms21M,-Xmx21M,那麼就不會觸發gc操做也不會出現異常了。
經過上面的實驗其實也從側面驗證了一個結論:當對象大於新生代剩餘內存的時候,將直接放入老年代,當老年代剩餘內存仍是沒法放下的時候,出發垃圾收集,收集後仍是不能放下就會拋出內存溢出異常了
咱們知道Hotspot jvm經過持久帶實現了Java虛擬機規範中的方法區,而運行時的常量池就是保存在方法區中的,所以持久帶溢出有多是運行時常量池溢出,也有多是方法區中保存的class對象沒有被及時回收掉或者class信息佔用的內存超過了咱們配置。當持久帶溢出的時候拋出java.lang.OutOfMemoryError: PermGen space
。
我在工做可能在以下幾種場景下出現此問題。
咱們知道Java中字符串常量是放在常量池中的,String.intern()這個方法運行的時候,會檢查常量池中是否存和本字符串相等的對象,若是存在直接返回對常量池中對象的引用,不存在的話,先把此字符串加入常量池,而後再返回字符串的引用。那麼咱們就能夠經過String.intern方法來模擬一下運行時常量區的溢出.下面咱們經過以下的代碼來模擬此種狀況:
1
2
3
4
5
6
7
8
9
10
11
12
|
import
java.util.*;
import
java.lang.*;
public
class
OOMTest{
public
static
void
main(String... args){
List<String> list =
new
ArrayList<String>();
while
(
true
){
list.add(UUID.randomUUID().toString().intern());
}
}
}
|
咱們經過以下的命令運行上面代碼:
java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest
運行後的輸入以下圖所示:
1
2
3
|
Exception in thread
"main"
java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at OOMTest.main(OOMTest.java:
8
)
|
經過上面的代碼,咱們成功模擬了運行時常量池溢出的狀況,從輸出中的PermGen space能夠看出確實是持久帶發生了溢出,這也驗證了,咱們前面說的Hotspot jvm經過持久帶來實現方法區的說法。
最後咱們在來看看java.lang.OutOfMemoryError:unable to create natvie thread
這種錯誤。 出現這種狀況的時候,通常是下面兩種狀況致使的:
線程棧總可用內存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序計數器佔用的內存
經過上面的公式咱們能夠看出,-Xmx 和 MaxPermSize的值越大,那麼留給線程棧可用的空間就越小,在-Xss參數配置的棧容量不變的狀況下,能夠建立的線程數也就越小。所以若是是由於這種狀況致使的unable to create native thread
,那麼要麼咱們增大進程所佔用的總內存,或者減小-Xmx或者-Xss來達到建立更多線程的目的。