Java虛擬機-內存溢出異常

內存泄露 內存溢出
 
內存泄露 通常是代碼設計存在缺陷致使的,指程序中動態分配內存給一些臨時對象,可是對象不會被GC所回收,它 始終佔用內存 。即被分配的對象可達可是已經無用;經過了解內存泄露的場 景,能夠避免沒必要要的內存溢出和提升本身的代碼水平;
 
內存泄露的幾種場景:
一、長生命週期的對象持有短生命週期對象的引用
      例如:在全局靜態map中緩存局部變量,且沒有清空操做,隨着時間的推移,這個map會愈來愈大,形成內存泄露;
二、修改hashset中對象的參數值,且參數是計算哈希值的字段
     當一個對象被存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段,不然對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了,在這種狀況下,即便在contains方法中使用該對象的當前引用做爲參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會致使沒法從HashSet集合中刪除當前對象,形成內存泄露;
三、機器的鏈接數和關閉時間設置;長時間開啓很是耗費資源的鏈接,也會形成內存泄漏。
 
內存溢出 指程序運行過程當中 沒法申請到足夠的內存 而致使的一種錯誤。內存溢出一般發生於Old段或者Perm段垃圾回收後,仍然無內存空間容納新的Java對象的狀況;經過了解內存溢出的集中常見狀況,能夠在出現內存溢出的時候快速定位問題的位置,縮短解決故障的時間。
 
內存溢出的幾種狀況:
一、堆內存溢出 OutOfMemoryError:java heap space
Java堆用於存儲對象實例,只要不斷地建立對象,而且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼在對象數量到達最大堆的容量限制後就會產生內存溢出異常;
在JVM規範中,堆中的內存是用來存放對象實例和數組的,若是細分,堆內存能夠分爲年輕代和年老代,年輕代又是包含1個Eden區和2個Survivor區;
當生成新對象時,內存的申請過程以下:
1.1:JVM先嚐試在Eden區分爲新對象所需的內存,若是內存大小足夠,那麼申請結束;
1.2:若是Eden區的內存不夠新對象所需的內存,JVM會啓動Minor GC,試圖將Eden區中不活躍的對象釋放掉,釋放後若Eden區仍然不足存放新的對象,則會試圖將Eden中活躍的對象放入Survivor區;
1.3:Survivor區被用來做爲Eden區和Old區的中間交換區域,當Old區空間足夠,Survivor區的對象會被移到Old區,不然會被保留在Survivor區;
1.4:若Old區的空間不足,那麼會觸發一次Mijor GC(Full GC)
1.5:Full GC後,若Survivor以及Old區仍然沒法存放從Eden複製過來的部分對象,致使JVM沒法在Eden區爲新對象建立內存區域,則會出現「OutOrMermoryError」錯誤;
二、虛擬機棧和本地方法棧溢出
在HotSpot虛擬機中是不區分虛擬機棧和本地方法棧,所以對於HotSpot來講,雖然-Xoss參數(設置本地方法棧)存在,實際上此參數是無效的,棧容量只由-Xss參數設置
Java虛擬機規範中對虛擬機棧和本地方法棧定義了2個異常:
2.1 若是線程請求的棧深度大於虛擬機所容許的棧深度,會拋出StackOverflowError異常;
2.2 若是虛擬機在擴展時,沒法申請到足夠的內存空間,會拋出OutOfMemoryError異常;
單線程中測試,不管是棧深度大於虛擬機容許的棧深度,仍是沒法申請到足夠的內存空間,都是拋出StackOverflowError異常;
1.使用-Xss參數減少棧內存容量,拋出StackOverflowError異常;2.定義大量的本地變量,增大此方法幀本地變量表的長度,拋出StackOverflowError異常;
若是測試不限於單線程,經過不斷創建線程的方式卻是能夠產生內存溢出異常;
虛擬機提供了參數來控制Java堆和方法區這兩個部份內存的最大值。
剩餘的內存-Xmx(最大堆容量)-MaxPermSize(最大方法區容量),程序計數器消耗內存較小。若是虛擬機進程自己消耗的內存不計算在內,那麼剩餘的內存就是被虛擬機棧和本地方法棧瓜分了。
每一個線程分配到的棧容量越大,能夠創建的線程數量天然越少,創建線程時就容易把剩下的內存耗盡;
若是是創建過多線程致使內存溢出,在不能減小線程數或者跟換64位虛擬機的狀況下,就只能經過減小最大堆和減小棧容量來換取更多的線程
 
三、方法區和運行時常量池溢出 OutOfMemoryError:PermGen space

運行時常量池是方法區的一部分。
JVM規範中,方法區主要是存放類的信息、常量、靜態變量等;
在JDK1.6及以前的版本中,運行時常量池分配在永久代中,可使用-XX:PermSize和-XX:MaxPermSize限制方法區大小,從而間接限制其中常量池的容量。
在測試運行結果中會看到,運行時常量池溢出,在OutOfMemoryError後面跟隨的提示信息是PermGen space,說明運行時常量池屬於方法區(HotSpot虛擬機中的永久代)的一部分;
jvm參數:-XX:PermSize=2m -XX:MaxPermSize=2m
將方法區的大小設置很低便可,在啓動加載類庫時就會出現內存不足的狀況
方法區用於存儲Class的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。對於此區域的測試,基本思路是運行時產生大量的類去填滿方法區,直到溢出;
若是程序加載的類過多,或者使用反射、gclib等這種動態代理生成類的技術,就可能致使該區發生內存溢出。
 
四、線程棧溢出java.lang.StackOverflowError
線程棧是線程獨有的一塊內存結構,因此線程棧發生問題一定是某個線程運行時產生的錯誤。
通常線程棧溢出是因爲遞歸太深或方法調用層級過多致使的。
發生棧溢出的錯誤信息爲:java.lang.StackOverflowError;
 
爲了不內存泄漏,在編寫代碼的過程當中能夠參考下面的建議:
一、儘早釋放無用對象的引用
二、使用字符串處理,避免使用String,應大量使用StringBuffer,每一個String對象都得獨立佔用內存一塊區域;
三、儘可能少用靜態變量,由於靜態變量存在的永久代(方法區),永久代基本不參與垃圾回收;
四、避免在循環中建立對象;
五、開啓大型文件或從數據庫一次拿了太多的數據很容易形成內存溢出,因此在這些地方要大概計算一下數據量的最大值是多少,而且設定所需最小及最大的內存空間值。
相關文章
相關標籤/搜索