1. 程式人生 > >Java常見記憶體溢位異常分析

Java常見記憶體溢位異常分析

http://www.importnew.com/14604.html

http://blog.csdn.net/znb769525443/article/details/50853712

簡介

Java虛擬機器規範規定JVM的記憶體分為了好幾塊,比如堆,棧,程式計數器,方法區等,而Hotspot jvm的實現中,將堆記憶體分為了三部分,新生代,老年代,持久帶,其中持久帶實現了規範中規定的方法區,而記憶體模型中不同的部分都會出現相應的OutOfMemoryError錯誤,接下來我們就分開來討論一下。java.lang.OutOfMemoryError這個錯誤我相信大部分開發人員都有遇到過,產生該錯誤的原因大都出於以下原因:

JVM記憶體過小、程式不嚴密,產生了過多的垃圾。

導致OutOfMemoryError異常的常見原因有以下幾種:

  1. 記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料;
  2. 集合類中有對物件的引用,使用完後未清空,使得JVM不能回收;
  3. 程式碼中存在死迴圈或迴圈產生過多重複的物件實體;
  4. 使用的第三方軟體中的BUG;
  5. 啟動引數記憶體值設定的過小;

此錯誤常見的錯誤提示:

  1. tomcat:java.lang.OutOfMemoryError: PermGen space
  2. tomcat:java.lang.OutOfMemoryError: Java heap space
  3. weblogic:Root cause of ServletException java.lang.OutOfMemoryError
  4. resin:java.lang.OutOfMemoryError
  5. java:java.lang.OutOfMemoryError

棧溢位(StackOverflowError)

棧溢位丟擲java.lang.StackOverflowError錯誤,出現此種情況是因為方法執行的時候棧的深度超過了虛擬機器容許的最大深度所致。出現這種情況,一般情況下是程式錯誤所致的,比如寫了一個死遞迴,就有可能造成此種情況。 下面我們通過一段程式碼來模擬一下此種情況的記憶體溢位。

  1. import java.util.*;  
  2. import java.lang.*;  
  3. publicclass OOMTest{  
  4.   publicvoid stackOverFlowMethod(){  
  5.       stackOverFlowMethod();  
  6.   }  
  7.   publicstaticvoid main(String... args){  
  8.       OOMTest oom = new OOMTest();  
  9.       oom.stackOverFlowMethod();  
  10.   }  
  11. }  
執行上面的程式碼,會丟擲如下的異常:
  1. Exception in thread "main" java.lang.StackOverflowError  
  2.         at OOMTest.stackOverFlowMethod(OOMTest.java:6)  

堆溢位(OutOfMemoryError:java heap space)

堆記憶體溢位的時候,虛擬機器會丟擲java.lang.OutOfMemoryError:java heap space,出現此種情況的時候,我們需要根據記憶體溢位的時候產生的dump檔案來具體分析(需要增加-XX:+HeapDumpOnOutOfMemoryErrorjvm啟動引數)。出現此種問題的時候有可能是記憶體洩露,也有可能是記憶體溢位了。
如果記憶體洩露,我們要找出洩露的物件是怎麼被GC ROOT引用起來,然後通過引用鏈來具體分析洩露的原因。
如果出現了記憶體溢位問題,這往往是程式本生需要的記憶體大於了我們給虛擬機器配置的記憶體,這種情況下,我們可以採用調大-Xmx來解決這種問題。

下面我們通過如下的程式碼來演示一下此種情況的溢位:

  1. import java.util.*;  
  2. import java.lang.*;  
  3. publicclass OOMTest{  
  4.         publicstaticvoid main(String... args){  
  5.                 List<byte[]> buffer = new ArrayList<byte[]>();  
  6.                 buffer.add(newbyte[10*1024*1024]);  
  7.         }  
  8. }  

我們通過如下的命令執行上面的程式碼:

java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest

程式輸入如下的資訊:
  1. [GC 1180K->366K(19456K), 0.0037311 secs]  
  2. [Full GC 366K->330K(19456K), 0.0098740 secs]  
  3. [Full GC 330K->292K(19456K), 0.0090244 secs]  
  4. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  
  5.         at OOMTest.main(OOMTest.java:7)  

從執行結果可以看出,JVM進行了一次Minor gc和兩次的Major gc,從Major gc的輸出可以看出,gc以後old區使用率為134K,而位元組陣列為10M,加起來大於了old generation的空間,所以丟擲了異常,如果調整-Xms21M,-Xmx21M,那麼就不會觸發gc操作也不會出現異常了。

通過上面的實驗其實也從側面驗證了一個結論:當物件大於新生代剩餘記憶體的時候,將直接放入老年代,當老年代剩餘記憶體還是無法放下的時候,觸發垃圾收集,收集後還是不能放下就會丟擲記憶體溢位異常了

持久帶溢位(OutOfMemoryError: PermGen space)

我們知道Hotspot jvm通過持久帶實現了Java虛擬機器規範中的方法區,而執行時的常量池就是儲存在方法區中的,因此持久帶溢位有可能是執行時常量池溢位,也有可能是方法區中儲存的class物件沒有被及時回收掉或者class資訊佔用的記憶體超過了我們配置。當持久帶溢位的時候丟擲java.lang.OutOfMemoryError: PermGen space
我在工作可能在如下幾種場景下出現此問題。

  1. 使用一些應用伺服器的熱部署的時候,我們就會遇到熱部署幾次以後發現記憶體溢位了,這種情況就是因為每次熱部署的後,原來的class沒有被解除安裝掉。
  2. 如果應用程式本身比較大,涉及的類庫比較多,但是我們分配給持久帶的記憶體(通過-XX:PermSize和-XX:MaxPermSize來設定)比較小的時候也可能出現此種問題。
  3. 一些第三方框架,比如spring,hibernate都通過位元組碼生成技術(比如CGLib)來實現一些增強的功能,這種情況可能需要更大的方法區來儲存動態生成的Class檔案。

我們知道Java中字串常量是放在常量池中的,String.intern()這個方法執行的時候,會檢查常量池中是否存和本字串相等的物件,如果存在直接返回對常量池中物件的引用,不存在的話,先把此字串加入常量池,然後再返回字串的引用。那麼我們就可以通過String.intern方法來模擬一下執行時常量區的溢位.下面我們通過如下的程式碼來模擬此種情況:

  1. import java.util.*;  
  2. import java.lang.*;  
  3. publicclass OOMTest{  
  4.         publicstaticvoid main(String... args){  
  5.                 List<String> list = new ArrayList<String>();  
  6.                 while(true){  
  7.                         list.add(UUID.randomUUID().toString().intern());  
  8.                 }  
  9.         }  
  10. }  

我們通過如下的命令執行上面程式碼:

java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest

執行後的輸入如下圖所示:

  1. Exception in thread "main" java.lang.OutOfMemoryError: PermGen space  
  2.         at java.lang.String.intern(Native Method)  
  3.         at OOMTest.main(OOMTest.java:8)  

通過上面的程式碼,我們成功模擬了執行時常量池溢位的情況,從輸出中的PermGen space可以看出確實是持久帶發生了溢位,這也驗證了,我們前面說的Hotspot jvm通過持久帶來實現方法區的說法。

OutOfMemoryError:unable to create native thread

最後我們在來看看java.lang.OutOfMemoryError:unable to create natvie thread這種錯誤。 出現這種情況的時候,一般是下面兩種情況導致的:

  1. 程式建立的執行緒數超過了作業系統的限制。對於Linux系統,我們可以通過ulimit -u來檢視此限制。
  2. 給虛擬機器分配的記憶體過大,導致建立執行緒的時候需要的native記憶體太少。我們都知道作業系統對每個程序的記憶體是有限制的,我們啟動Jvm,相當於啟動了一個程序,假如我們一個程序佔用了4G的記憶體,那麼通過下面的公式計算出來的剩餘記憶體就是建立執行緒棧的時候可以用的記憶體。 執行緒棧總可用記憶體=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程式計數器佔用的記憶體 通過上面的公式我們可以看出,-Xmx 和 MaxPermSize的值越大,那麼留給執行緒棧可用的空間就越小,在-Xss引數配置的棧容量不變的情況下,可以建立的執行緒數也就越小。因此如果是因為這種情況導致的unable to create native thread,那麼要麼我們增大程序所佔用的總記憶體,或者減少-Xmx或者-Xss來達到建立更多執行緒的目的。

相關推薦

Java常見記憶體溢位異常分析

http://www.importnew.com/14604.html http://blog.csdn.net/znb769525443/article/details/50853712 簡介 Java虛擬機器規範規定JVM的記憶體分為了好幾塊,比如堆,棧,程式計數

java常見記憶體溢位情形

虛擬機器棧溢位(如果虛擬機器在擴充套件時無法申請到足夠的記憶體空間將丟擲OutOfMemoryError)package com.jvm.memory; import java.util.ArrayList; import java.util.List; public

Java常見記憶體溢位(OOM)解決方案

1,         程式計數器 一塊很小的記憶體空間,作用是當前執行緒所執行的位元組碼的行號指示器。 2,         java棧 與程式計數器一樣,java棧(虛擬機器棧)也是執行緒私有的,其生命週期與執行緒相同。通常存放基本資料型別,物件引用(一個指向物件起始地址

Java記憶體溢位梗概分析

任何使用過基於 Java 的企業級後端應用的軟體開發者都會遇到過這種低劣、奇怪的報錯,這些報錯來自於使用者或是測試工程師: java.lang.OutOfMemoryError:Java heap space。為了弄清楚問題,我們必須返回到演算法複雜性的電腦科學基礎,尤其是“

JVM解讀(四):JVM記憶體溢位異常分析

JVM全稱是java Virtual Machine(java虛擬機器),JVM遮蔽了與各個計算機平臺相關的軟體和硬體差異。 在接下來的日子裡,通過寫部落格的形式學習JVM,讓自己更懂得Java! 本系列文章是對《深入分析javaweb技術內幕》和《深入理解

java關於記憶體溢位分析,mat工具

對於我們在現實的場景中,或多或少會遇到記憶體溢位的問題,可怎麼排查這個問題呢?首先我們需要了解一下關於java中自身安裝自帶的幾個命令:jmap,Jvisualvm,jstack等。一般情況下,對於記憶體,cpu,執行緒的監控用Jvisualvm這個命令我們很清晰的能檢視系統

Java常見記憶體溢位異常及解決方法

Java的記憶體溢位,主要是兩方面的異常:堆記憶體溢位、非堆記憶體溢位。 一、 java.lang.OutOfMemoryError: Java heap space。 Heap size 設定 JVM堆的設定是指java程式執行過程中JVM可以調配使用的記憶體空間的設定

學習筆記1:深入理解Java虛擬機器——JVM高階特性與最佳實踐_走進java_java記憶體區域與記憶體溢位異常

第一部分:走進java Java虛擬機器 程式碼在華章下載 jdk釋出了六個命令列工具和兩個視覺化故障處理工具。 推薦書籍 設計原本 領域特定語言 現在著名的Java虛擬機器 hotspot vm(預設) jrockit vm j9 vm jdk sun jdk op

java虛擬機器—-java記憶體區域與記憶體溢位異常

一,java虛擬機器所管理的執行時資料區域分為:程式計數器、java虛擬機器棧、本地方法棧、java堆、方法區、執行時常量池。                     1,程式計數器:

《深入理解Java虛擬機器—JVM高階特性與實踐 周志明 著》之第2章 Java記憶體區域與記憶體溢位異常

1、Java虛擬機器所管理的記憶體包括以下幾個執行時資料區域: 2、程式計數器:          1. 可以看作是當前執行緒所執行的位元組碼的行號指示器,是一塊較小的記憶體空間;  &nbs

JAVA虛擬機器(JVM)劃重點 第二章 Java記憶體區域與記憶體溢位異常 之 虛擬機器物件

本部落格參考《深入理解Java虛擬機器》(第二版)一書,提取重點知識,再加以個人的理解編寫而成。轉載請標明來源。 JAVA虛擬機器(JVM)劃重點 第二章 Java記憶體區域與記憶體溢位異常 之 虛擬機器物件 Java物件的建立 1、類載入過程

JAVA虛擬機器(JVM)劃重點 第二章 Java記憶體區域與記憶體溢位異常 之 執行時資料區域

本部落格參考《深入理解Java虛擬機器》(第二版)一書,提取重點知識,再加以個人的理解編寫而成。轉載請標明來源。 JVM劃重點 第二章 Java記憶體區域與記憶體溢位異常 之 執行時資料區域 概述 執行時資料區域 程式計數器 Java虛擬機

【夾娃系列】java面試基礎知識儲備(¥2)——JVM記憶體劃分和記憶體溢位異常的原因和解決方法

JVM記憶體劃分和記憶體溢位 JVM記憶體劃分 記憶體溢位的異常和解決辦法 JVM記憶體劃分 堆:存放物件例項,被所有的執行緒共享的一塊區域。垃圾收集器管理的主要區域。 方法區:儲存虛擬機器載入的類資訊,常量,靜態變

《深入理解java虛擬機器》讀書筆記(二)---- Java記憶體區域與記憶體溢位異常

執行時資料區域 java虛擬機器所管理的記憶體將會包括以下幾個執行時資料區域: 1、程式計數器 程式計數器是一塊較小的記憶體空間,它可以看作是當前執行緒所執行位元組碼的行號指示器。在虛擬機器的概念模型裡,位元組碼直譯器的工作就是通過改變這個計數器的值來選取下一條需要執

虛擬機器學習之一:java記憶體區域與記憶體溢位異常

1.執行時資料區域 java虛擬機器在執行java程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。這些區域都有各自的用途和建立、銷燬時間,有的區域伴隨虛擬機器程序的啟動而存在,有些區域則依賴使用者執行緒的啟動和結束而建立和銷燬。 1.1程式計數器 程式計數器

java記憶體區域與記憶體溢位異常

執行是記憶體區域 java虛擬機器在執行java程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。 1.執行緒獨有的記憶體區域 (1)程式計數器(Program Counter Register) 這塊記憶體區域很小,它是當前執行緒所執行的位元組碼的行號

Java記憶體溢位異常

Java堆溢位 Java堆用於儲存物件例項,只要不斷的建立物件,並且保證GC Roots到物件之間有可達路徑來避免垃圾回收機制清楚這些物件,那麼在物件數量到達最大對的容量限制後就會產生記憶體溢位異常。 package com.xrq.test; import java.

Java 記憶體區域與記憶體溢位異常(二)

一、虛擬機器中Java物件的建立 語言層面上,建立Java物件通常僅僅是一個new關鍵字而已。 在虛擬遇到new指令時: 1、首先檢查這個指令的引數是否能在常量池中定位到一個類的符號引用,並檢查這個類的符號引用代表的類是否已經載入,解析和初始化過。如果沒有,則必須執行類

Java 記憶體區域與記憶體溢位異常(三)

實戰:OutOfMemoryError 異常 參考:《深入理解Java虛擬機器》-jvm高階特性與最佳實現(周志明著) 之前的兩篇中介紹Java虛擬機器中各個執行時記憶體區域的作用,這節中通過人為異常的方式驗證各個執行時區儲存的內容 一、Java堆溢位 Java堆中用

Java記憶體區域與記憶體溢位異常簡單總結

目錄 1.簡述 2.作用 3.注意 1.簡介 3.異常 1.簡述 1. 簡述 2.異常 1.簡述 2.異常 1.JVM執行時資料區域簡圖 Java虛擬機器執行時資料區 2.程式計數器(Program Co