深入理解java虛擬機 摘要(三)--實戰(zhàn):OutOfMemoryError異常

深入理解java虛擬機 摘要


目錄:
一、自動內(nèi)存管理機制

  1. 運行時數(shù)據(jù)區(qū)域
  2. HotSpot虛擬機對象探秘
  3. 實戰(zhàn):OutOfMemoryError異常
  4. 垃圾收集器與內(nèi)存分配策略

一、自動內(nèi)存管理機制


3. 實戰(zhàn):OutOfMemoryError異常

  • Java堆溢出:

    測試代碼:

    public class Tests {
       static class Obj{
       }
    
       public static  void  main(String[] args) throws >Exception   {
           List<Obj> list = new ArrayList<>();
           while (true){
               list.add(new Obj());
           }
       }
    }   
    

    啟動參數(shù):

    -Xmx20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
    

    將堆的最小值-Xms參數(shù)與最大值-Xmx參數(shù)設(shè)置為一樣即可避免堆自動擴展,通過參數(shù)-XX:+HeapDumpOnOutOfMemoryError可以讓虛擬機在出現(xiàn)內(nèi)存溢
    出異常時Dump出當(dāng)前的內(nèi)存堆轉(zhuǎn)儲快照以便事后進行分析

    要解決這個區(qū)域的異常,一般的手段是先通過內(nèi)存映像分析工具
    ideal可使用JProfiler和JMeter插件進行分析

  • 虛擬機棧和本地方法棧溢出

    測試代碼:

    public static class JavaVMStackSOF {
       private int stackLength = 1;
    
       public void stackLeak() {
           stackLength++;
           stackLeak();
       }
    }
    
       public static void main(String[]args)throws Throwable{
           JavaVMStackSOF oom=new JavaVMStackSOF();
           try{
               oom.stackLeak();
           }catch(Throwable e){
               System.out.println("stack length:"+oom.stackLength);
               throw e;
           }
       }
    

    啟動參數(shù):

    -Xss20m
    

    對于HotSpot來說,雖然-Xoss參數(shù)(設(shè)置本地方法棧大?。┐嬖?,但實際上是無效的,棧容量只由-Xss參數(shù)設(shè)定
    如果線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError異常。

    如果虛擬機在擴展棧時無法申請到足夠的內(nèi)存空間,則拋出OutOfMemoryError異常。

    這里把異常分成兩種情況,看似更加嚴謹,但卻存在著一些互相重疊的地方:當(dāng)??臻g無法繼續(xù)分配時,到底是內(nèi)存太小,還是已使用的??臻g太大,其本質(zhì)上只是對同一件事情的兩種描述而已。

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

  • 本機直接內(nèi)存溢出

    DirectMemory容量可通過-XX:MaxDirectMemorySize指定,如果不指定,則默認與Java堆最大值(-Xmx指定)一樣

    測試代碼:

    public class Tests {
      private static final Long MB=1024L*1024L;
      public static void main(String[]args)throws Exception{
          Field unsafeField=Unsafe.class.getDeclaredFields()[0];
          unsafeField.setAccessible(true);
          Unsafe unsafe=(Unsafe)unsafeField.get(null);
          while(true){
              unsafe.allocateMemory(MB);
          }
      }
    }
    

    啟動參數(shù):

    -Xss20m
    

    代碼解釋:
    越過了DirectByteBuffer類,直接通過反射獲取Unsafe實例進行內(nèi)存分配(Unsafe類的getUnsafe()方法限制了只有引導(dǎo)類加載器才會返回實例,也就是設(shè)計者希望只有rt.jar中的類才能使用Unsafe的功能)。因為,雖然使用DirectByteBuffer分配內(nèi)存也會拋出內(nèi)存溢出異常,但它拋出異常時并沒有真正向操作系統(tǒng)申請分配內(nèi)存,而是通過計算得知內(nèi)存無法分配,于是手動拋出異常,真正申請分配內(nèi)存的方法是unsafe.allocateMemory()。

    由DirectMemory導(dǎo)致的內(nèi)存溢出,一個明顯的特征是在Heap Dump文件中不會看見明顯的異常,如果讀者發(fā)現(xiàn)OOM之后Dump文件很小,而程序中又直接或間接使用了NIO,那就可以考慮檢查一下是不是這方面的原因。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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