深入JAVA虛擬機(jī)2_4OutOfMemoryError異常


layout: post

title: 深入JAVA虛擬機(jī)2_4OutOfMemoryError異常

categories: JVM JAVA

description: 深入JAVA虛擬機(jī)2_4OutOfMemoryError異常

keywords: JVM JAVA

注意:KOTLIN跟JAVA運(yùn)行結(jié)果可能會不同,參照樣例
http://www.itdecent.cn/p/d9f90f3ee936


2_4_1. JAVA堆溢出測試

測試思路

java堆用于存儲對象實(shí)例,只要不斷創(chuàng)建對象,并保證
GC Roots到對象之間有可達(dá)路徑來避免垃圾回收機(jī)制清
除這些對象,那么在對象數(shù)量到達(dá)最大堆的容量限制后
就會產(chǎn)生內(nèi)存溢出異常

code2_3虛擬機(jī)參數(shù)

//限制java堆的大小為20mb[-Xms為堆的最小值,-Xmx為
堆的最大值]
//XX:+HeapDumpOnOutOfMemoryError可讓虛擬機(jī)在出
現(xiàn)內(nèi)存溢出異常時(shí)Dump出當(dāng)前內(nèi)存堆轉(zhuǎn)儲快照以便事后
進(jìn)行分析
參數(shù):  -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

code2_3 代碼

package num2

/**
 * Created by Joey_Tsai on 2018/3/5.
 */
class HeapOOM{
    companion object {
        class OOMObject{

        }
    }
}

fun main(args : Array<String>){
    val list : MutableList<HeapOOM.Companion.OOMObject> = ArrayList<HeapOOM.Companion.OOMObject>();
    var i = 0;
    while (true){
        list.add(HeapOOM.Companion.OOMObject());

        println(++i);
    }
}

內(nèi)存泄漏 與 內(nèi)存溢出

內(nèi)存泄漏:編寫的程序沒有正確的釋放內(nèi)存
內(nèi)存溢出:內(nèi)存不足,無法正常給程序分配內(nèi)存,導(dǎo)致內(nèi)
存不足的原因有很多,內(nèi)存泄漏只是其中的一種

解決方法

使用工具檢查GC Roots查看引用鏈上的對象是否都有存
活的意義,若確實(shí)沒有出現(xiàn)泄露的情況,應(yīng)該調(diào)整堆內(nèi)
存參數(shù)(-Xmx 和 Xms)的最大值和最小值

p.s

程序運(yùn)行參數(shù)圖

程序運(yùn)行結(jié)果圖

github鏈接(使用kotlin實(shí)現(xiàn))
https://github.com/joeytsai03/JVMStudy/blob/master/src/num2/code2_3.kt



                          分    割    線


2_4_2. 虛擬機(jī)棧和本地方法棧溢出

1.虛擬機(jī)棧和本地方法棧OOM測試

測試思路

HotSpot虛擬機(jī)中不分虛擬機(jī)棧和本地方法棧,故-Xoss
參數(shù)(設(shè)置本地方法棧大小)存在但實(shí)際上是無效的,棧容
量由-Xss參數(shù)設(shè)定,關(guān)于虛擬機(jī)棧和本地方法棧java虛擬
機(jī)描述了兩種異常:
1.如果線程請求的棧深度大于虛擬機(jī)所允許的最大深度,
  將拋出StackOverflowError異常
2.如果虛擬機(jī)在擴(kuò)展棧時(shí)無法申請到足夠的內(nèi)存空間,則
  拋出OutOfMemoryError異常

code2_4虛擬機(jī)參數(shù)

參數(shù) :   -Xss128k

code2_4代碼

package num2

/**
 * Created by Joey_Tsai on 2018/3/5.
 * VM:-Xss128k
 */
public class JavaVMStackSOF{
    public var stackLength : Int = 1
    public fun stackLeak() : Unit{
        stackLength++;
        stackLeak()
    }
}
fun main(args:Array<String>){
    val javaVMStackSOF : JavaVMStackSOF = JavaVMStackSOF()
   try {

       javaVMStackSOF.stackLeak()

   }catch (e : Throwable ){
       println("stack length : ${javaVMStackSOF.stackLength}")
       throw e
   }
}

java虛擬機(jī)棧 與 java堆 與 方法區(qū)

1.java虛擬機(jī)棧:線程私有,生命周期與線程相同,每個(gè)方
法在執(zhí)行的過程中都會創(chuàng)建一個(gè)棧幀(Stack Frame)用于
存儲局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接,方法出口等信
息。每一個(gè)方法執(zhí)行完成的過程,就對應(yīng)一個(gè)棧幀在虛
擬機(jī)棧中入棧出棧的過程。

2.java堆:所有線程共享的一塊內(nèi)存區(qū)域,用于存放對象
實(shí)例,垃圾收集器管理的主要區(qū)域,很多時(shí)候也被稱作
"GC堆"(Garbage Collected Heap)

3.方法區(qū):所有線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬
機(jī)加載的類信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后
的代碼等數(shù)據(jù)

實(shí)驗(yàn)結(jié)果

在單線程下,無論由于棧幀太大還是虛擬機(jī)棧容量太
小,當(dāng)內(nèi)存無法分配時(shí)虛擬機(jī)拋出的都是
StackOverflowError異常

p.s


程序運(yùn)行參數(shù)圖

程序運(yùn)行結(jié)果圖

github鏈接(使用kotlin實(shí)現(xiàn))
https://github.com/joeytsai03/JVMStudy/blob/master/src/num2/code2_4.kt



2.多線程導(dǎo)致內(nèi)存溢出異常

測試思路

如果測試時(shí)不限單線程,通過不斷創(chuàng)建線程的方式倒是
可以產(chǎn)生內(nèi)存溢出異常,這種情況下,為每個(gè)線程的棧
分配的內(nèi)存越大,反而越容易產(chǎn)生內(nèi)存溢出異常

使用工具

JProfiler用于查看線程使用情況
Idea安裝JProfiler

code2_5虛擬機(jī)參數(shù)

參數(shù):     -Xss2M

code2_5代碼

package num2

/**
 * Created by Joey_Tsai on 2018/3/6.
 *VM: -Xss200M
 */
class JavaVMStackOOM{
    private fun dontStop() : Unit{
        while (true){

        }
    }
    public fun stackLeakByThread():Unit{
        while (true){
            val thread : Thread = Thread(Runnable(){
                @Override
                fun run(){
                    dontStop()
                }
            });
            thread.start()
        }
    }
}

fun main(args : Array<String>){
    val oom = JavaVMStackOOM()
    oom.stackLeakByThread()
}

操作系統(tǒng)內(nèi)存分配

譬如,在32位的windows系統(tǒng)中給每個(gè)線程分配的內(nèi)存
限制為2g,虛擬機(jī)提供了參數(shù)來控制java堆和方法區(qū)這
兩部分內(nèi)存的最大值,剩余的內(nèi)存為2GB減去Xmx(最大
堆容量),再減去MaxPermSize(最大方法區(qū)容量),程序
計(jì)數(shù)器消耗內(nèi)存很小,可以忽略。

p.s


程序運(yùn)行參數(shù)圖

github鏈接(使用kotlin實(shí)現(xiàn))
https://github.com/joeytsai03/JVMStudy/blob/master/src/num2/code2_5.kt



                          分    割    線


2_4_3 方法區(qū)和運(yùn)行時(shí)常量池溢出

1.運(yùn)行時(shí)常量池導(dǎo)致的內(nèi)存溢出異常

測試思路

運(yùn)行時(shí)常量池是方法區(qū)的一部分,jdk7開始逐步去除永久
代,String.intern()是一個(gè)Native方法,在jdk1.6及之前的版
本中,由于常量池分配在永久代中,我們可以通過
-XX:PermSize與 -XX:MaxPermSize限制方法區(qū)大小,從
而限制常量池容量大小

code2_6虛擬機(jī)參數(shù)

VM:-XX:PermSize=10M -XX:MaxPermSize=10M

code2_6代碼

package num2

/**
 * Created by Joey_Tsai on 2018/3/6.
 * VM:-XX:PermSize=10M -XX:MaxPermSize=10M
 */
class RuntimeConstantPoolOOM2_6{

}
fun main(args : Array<String>){
    //使用List保持著常量池引用,避免Full GC回收常量池行為
    val list : MutableList<String>?=ArrayList<String>();
    //10MB的permSize在integer范圍內(nèi)足夠產(chǎn)生OOM了
    var i : Int = 0
    while (true){
        list?.add(i++.toString().intern())
        println(i)
    }
}

實(shí)驗(yàn)結(jié)果

運(yùn)行時(shí)常量池溢出,在"OutOfMemoryError"后面跟著的提示信息
是"PermGen space",說明運(yùn)行時(shí)常量池屬于方法區(qū)(HotSpot虛擬機(jī)中的永
久代)的一部分,在jdk1.7中則不會得到相同結(jié)果,while將一直循環(huán)下去。

2.String.intern返回引用測試

code2_7代碼

package num2

/**
 * Created by Joey_Tsai on 2018/3/6.
 */
public class RuntimeConstantPoolOOM2_7{
    
}

fun main(args: Array<String>) {
    val str1 : String = StringBuilder("計(jì)算機(jī)").append("軟件").toString()
    println(str1.intern() == str1)

    val str2 : String = StringBuilder("ja").append("va").toString()
    println(str2.intern() == str2)
}

JDK1.6中的intern() 與 JDK1.7中的intern()

在JDK1.6中,intern()會把首次遇到的字符串實(shí)例復(fù)制在永久代中,返回的
也是永久代中這個(gè)字符串實(shí)例的引用,而StringBuilder創(chuàng)建的字符串實(shí)例
在java堆上,所以必然不是同一個(gè)引用,將返回false。而JDK1.7中的
intern()實(shí)現(xiàn)不會再復(fù)制實(shí)例,只是在常量池中記錄首次出現(xiàn)的引用,因此
intern()返回的引用和由StringBuilder創(chuàng)建的那個(gè)字符串實(shí)例是同一個(gè)。對
于str2返回false是因?yàn)?java'這個(gè)字符串在執(zhí)行StringBuilder.toString()之前
已經(jīng)出現(xiàn)過,字符串常量池已經(jīng)有它的引用,不符合首次出現(xiàn)原則,而'計(jì)
算機(jī)軟件'這個(gè)字符串則是首次出現(xiàn)返回true。  
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容