<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JVM內(nèi)存溢出詳解

          共 5267字,需瀏覽 11分鐘

           ·

          2024-04-11 10:28

          內(nèi)存溢出和內(nèi)存泄漏的區(qū)別:

          內(nèi)存溢出 out of memory ,是指程序在申請(qǐng)內(nèi)存時(shí),沒(méi)有足夠的內(nèi)存空間供其使用,出現(xiàn) out of memory ;比如申請(qǐng)了一個(gè) integer, 但給它存了 long 才能存下的數(shù),那就是內(nèi)存溢出。

          內(nèi)存泄露 memory leak ,是指程序在申請(qǐng)內(nèi)存后,無(wú)法釋放已申請(qǐng)的內(nèi)存空間,一次內(nèi)存泄露危害可以 忽略,但內(nèi)存泄露堆積后果很?chē)?yán)重,無(wú)論多少內(nèi)存 , 遲早會(huì)被占光。

          memory leak 會(huì)最終會(huì)導(dǎo)致 out of memory

          內(nèi)存溢出就是你要求分配的內(nèi)存超出了系統(tǒng)能給你的,系統(tǒng)不能滿足需求,于是產(chǎn)生溢出。

          內(nèi)存泄漏是指你向系統(tǒng)申請(qǐng)分配內(nèi)存進(jìn)行使用 (new) ,可是使用完了以后卻不歸還 (delete) ,結(jié)果你申請(qǐng) 到的那塊內(nèi)存你自己也不能再訪問(wèn)(也許你把它的地址給弄丟了),而系統(tǒng)也不能再次將它分配給需要 的程序。一個(gè)盤(pán)子用盡各種方法只能裝 4 個(gè)果子,你裝了 5 個(gè),結(jié)果掉倒地上不能吃了。這就是溢出!比 方說(shuō)棧,棧滿時(shí)再做進(jìn)棧必定產(chǎn)生空間溢出,叫上溢,棧空時(shí)再做退棧也產(chǎn)生空間溢出,稱為下溢。就 是分配的內(nèi)存不足以放下數(shù)據(jù)項(xiàng)序列 , 稱為內(nèi)存溢出。

          全文簡(jiǎn)短總結(jié),具體內(nèi)容可以看下文。

          棧內(nèi)存溢出 (StackOverflowError)

          程序所要求的棧深度過(guò)大導(dǎo)致,可以寫(xiě)一個(gè)死遞歸程序觸發(fā)。

          堆內(nèi)存溢出 (OutOfMemoryError:java heap space)

          • 分清內(nèi)存溢出還是內(nèi)存泄漏

          • 泄露則看對(duì)象如何被 GC Root 引用。

          • 溢出則通過(guò) 調(diào)大 -Xms -Xmx 參數(shù)。

          持久帶內(nèi)存溢出 (OutOfMemoryError: PermGen space)

          • 持久帶中包含方法區(qū),方法區(qū)包含常量池

          • 因此持久帶溢出有可能是運(yùn)行時(shí)常量池溢出,也有可能是方法區(qū)中保存的 class 對(duì)象沒(méi)有被及時(shí)回收 掉或者 class 信息占用的內(nèi)存超過(guò)了我們配置

          • String.intern() 觸發(fā)常量池溢出

          • Class 對(duì)象未被釋放, Class 對(duì)象占用信息過(guò)多,有過(guò)多的 Class 對(duì)象。可以導(dǎo)致持久帶內(nèi)存溢出

          無(wú)法創(chuàng)建本地線程

          總?cè)萘坎蛔儯褍?nèi)存,非堆內(nèi)存設(shè)置過(guò)大,會(huì)導(dǎo)致能給線程的內(nèi)存不足。

          以下是詳細(xì)內(nèi)容

          棧溢出 (StackOverflowError)

          棧溢出拋出 StackOverflowError 錯(cuò)誤, 出現(xiàn)此種情況是因?yàn)榉椒ㄟ\(yùn)行的時(shí)候棧的深度超過(guò)了虛擬機(jī)容許 的最大深度所致 。出現(xiàn)這種情況,一般情況下是程序錯(cuò)誤所致的,比如寫(xiě)了一個(gè)死遞歸,就有可能造成 此種情況。 下面我們通過(guò)一段代碼來(lái)模擬一下此種情況的內(nèi)存溢出。

          import java.util.*;
          import java.lang.*;
          public class OOMTest{
          public void stackOverFlowMethod(){
          stackOverFlowMethod();
          }
          public static void main(String... args){
          OOMTest oom = new OOMTest();
          oom.stackOverFlowMethod();
          }
          }


          運(yùn)行上面的代碼,會(huì)拋出如下的異常:

          Exception in thread "main" java.lang.StackOverflowError
          at OOMTest.stackOverFlowMethod(OOMTest.java:6)

          對(duì)于棧內(nèi)存溢出,根據(jù)《 Java 虛擬機(jī)規(guī)范》中文版:

          如果線程請(qǐng)求的棧容量超過(guò)棧允許的最大容量的話, Java 虛擬機(jī)將拋出一個(gè) StackOverflow 異常; 如果 Java 虛擬機(jī)棧可以動(dòng)態(tài)擴(kuò)展,并且擴(kuò)展的動(dòng)作已經(jīng)嘗試過(guò),但是無(wú)法申請(qǐng)到足夠的內(nèi)存去完成 擴(kuò)展,或者在新建立線程的時(shí)候沒(méi)有足夠的內(nèi)存去創(chuàng)建對(duì)應(yīng)的虛擬機(jī)棧,那么 Java 虛擬機(jī)將拋出一 個(gè) OutOfMemory 異常。

          堆溢出 (OutOfMemoryError:java heap space)

          堆內(nèi)存溢出的時(shí)候,虛擬機(jī)會(huì)拋出 java.lang.OutOfMemoryError:java heap space, 出現(xiàn)此種情況的時(shí) 候,我們需要根據(jù)內(nèi)存溢出的時(shí)候產(chǎn)生的 dump 文件來(lái)具體分析(需要增加 - XX:+HeapDumpOnOutOfMemoryErrorjvm 啟動(dòng)參數(shù))。 出現(xiàn)此種問(wèn)題的時(shí)候有可能是內(nèi)存泄露,也有 可能是內(nèi)存溢出了。

          • 如果內(nèi)存泄露,我們要找出泄露的對(duì)象是怎么被 GC ROOT 引用起來(lái),然后通過(guò)引用鏈來(lái)具體分析 泄露的原因。

          • 如果出現(xiàn)了內(nèi)存溢出問(wèn)題,這往往是程序本生需要的內(nèi)存大于了我們給虛擬機(jī)配置的內(nèi)存,這種情 況下,我們可以采用調(diào)大 -Xmx 來(lái)解決這種問(wèn)題。下面我們通過(guò)如下的代碼來(lái)演示一下此種情況的 溢出:

          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]);
          }
          }

          我們通過(guò)如下的命令運(yùn)行上面的代碼:

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

          序輸出如下的信息:

          [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)


          從運(yùn)行結(jié)果可以看出, JVM 進(jìn)行了一次 Minor gc 和兩次的 Major gc ,從 Major gc 的輸出可以看出, gc old 區(qū)使用率為 134K ,而字節(jié)數(shù)組為 10M ,加起來(lái)大于了 old generation 的空間,所以拋出了異常, 如果調(diào)整 -Xms21M,-Xmx21M, 那么就不會(huì)觸發(fā) gc 操作也不會(huì)出現(xiàn)異常了。

          通過(guò)上面的實(shí)驗(yàn)其實(shí)也從側(cè)面驗(yàn)證了一個(gè)結(jié)論: 對(duì)象大于新生代剩余內(nèi)存的時(shí)候,將直接放入老年代, 當(dāng) 老年代剩余內(nèi)存還是無(wú)法放下的時(shí)候,觸發(fā)垃圾收集,收集后還是不能放下就會(huì)拋出內(nèi)存溢出異常 了。

          持久帶溢出 (OutOfMemoryError: PermGen space)

          我們知道 Hotspot jvm 通過(guò)持久帶實(shí)現(xiàn)了 Java 虛擬機(jī)規(guī)范中的方法區(qū),而運(yùn)行時(shí)的常量池就是保存在方法 區(qū)中的,因此持久帶溢出有可能是運(yùn)行時(shí)常量池溢出,也有可能是方法區(qū)中保存的 class 對(duì)象沒(méi)有被及時(shí) 回收掉或者 class 信息占用的內(nèi)存超過(guò)了我們配置。

          當(dāng)持久帶溢出的時(shí)候拋出 java.lang.OutOfMemoryError: PermGen space 。可能在如下幾種場(chǎng)景下出 現(xiàn):

          1. 使用一些應(yīng)用服務(wù)器的熱部署的時(shí)候,我們就會(huì)遇到熱部署幾次以后發(fā)現(xiàn)內(nèi)存溢出了,這種情況就 是因?yàn)槊看螣岵渴鸬暮螅瓉?lái)的 class 沒(méi)有被卸載掉。

          2. 如果應(yīng)用程序本身比較大,涉及的類(lèi)庫(kù)比較多,但是我們分配給持久帶的內(nèi)存(通過(guò) -XX:PermSize -XX:MaxPermSize 來(lái)設(shè)置)比較小的時(shí)候也可能出現(xiàn)此種問(wèn)題。

          3. 一些第三方框架,比如 spring,hibernate 都通過(guò)字節(jié)碼生成技術(shù)(比如 CGLib )來(lái)實(shí)現(xiàn)一些增強(qiáng)的 功能,這種情況可能需要更大的方法區(qū)來(lái)存儲(chǔ)動(dòng)態(tài)生成的 Class 文件。

          我們知道 Java 中字符串常量是放在常量池中的, String.intern() 這個(gè)方法運(yùn)行的時(shí)候,會(huì)檢查常量池 中是否存和本字符串相等的對(duì)象,如果存在直接返回對(duì)常量池中對(duì)象的引用,不存在的話,先把此 字符串加入常量池,然后再返回字符串的引用。那么我們就可以通過(guò) String.intern 方法來(lái)模擬一下 運(yùn)行時(shí)常量區(qū)的溢出 . 下面我們通過(guò)如下的代碼來(lái)模擬此種情況:

          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());
          }
          }
          }

          我們通過(guò)如下的命令運(yùn)行上面代碼:

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


          運(yùn)行后的輸入如下圖所示 :

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

          通過(guò)上面的代碼,我們成功模擬了運(yùn)行時(shí)常量池溢出的情況,從輸出中的 PermGen space 可以看出確實(shí) 是持久帶發(fā)生了溢出,這也驗(yàn)證了,我們前面說(shuō)的 Hotspot jvm 通過(guò)持久帶來(lái)實(shí)現(xiàn)方法區(qū)的說(shuō)法。

          OutOfMemoryError:unable to create native thread

          最后我們?cè)趤?lái)看看 java.lang.OutOfMemoryError:unable to create natvie thread 這種錯(cuò)誤。出現(xiàn)這種

          情況的時(shí)候,一般是下面兩種情況導(dǎo)致的:

          1. 程序創(chuàng)建的線程數(shù)超過(guò)了操作系統(tǒng)的限制。對(duì)于 Linux 系統(tǒng),我們可以通過(guò) ulimit -u 來(lái)查看此限 制。

          2. 給虛擬機(jī)分配的內(nèi)存過(guò)大,導(dǎo)致創(chuàng)建線程的時(shí)候需要的 native 內(nèi)存太少。

          我們都知道操作系統(tǒng)對(duì)每個(gè)進(jìn)程的內(nèi)存是有限制的,我們啟動(dòng) Jvm, 相當(dāng)于啟動(dòng)了一個(gè)進(jìn)程,假如我們一 個(gè)進(jìn)程占用了 4G 的內(nèi)存,那么通過(guò)下面的公式計(jì)算出來(lái)的剩余內(nèi)存就是建立線程棧的時(shí)候可以用的內(nèi) 存。 線程棧總可用內(nèi)存 =4G- -Xmx 的值) - -XX:MaxPermSize 的值) - 程序計(jì)數(shù)器占用的內(nèi)存, 通過(guò)上面的公式我們可以看出, -Xmx MaxPermSize 的值越大,那么留給線程棧可用的空間就越小, -Xss 參數(shù)配置的棧容量不變的情況下,可以創(chuàng)建的線程數(shù)也就越小。因此如果是因?yàn)檫@種情況導(dǎo)致的 unable to create native thread, 那么要么我們?cè)龃筮M(jìn)程所占用的總內(nèi)存,或者減少 -Xmx 或者 -Xss 來(lái)達(dá)到 創(chuàng)建更多線程的目的。

          end


          * 版權(quán)聲明: 轉(zhuǎn)載文章和圖片均來(lái)自公開(kāi)網(wǎng)絡(luò),版權(quán)歸作者本人所有,推送文章除非無(wú)法確認(rèn),我們都會(huì)注明作者和來(lái)源。如果出處有誤或侵犯到原作者權(quán)益,請(qǐng)與我們聯(lián)系刪除或授權(quán)事宜。


          長(zhǎng)按識(shí)別圖中二維碼

          關(guān)注獲取更多資訊



          不點(diǎn)關(guān)注,我們哪來(lái)故事?




          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本一区三区祀频在线观看 | 亚洲人人草 | 国产做爱视频网站18 | 高清+国产无码在线观看 | 亚洲一区二区免费视频 |