深入理解jvm之內存區域與內存溢出

jopen 8年前發布 | 20K 次閱讀 Java開發 JVM

</div>

文章目錄

  1. 1. Java內存區域與內存溢出異常

    1. 1.1. 運行時數據區域

      1. 1.1.1. 程序計數器
      2. 1.1.2. java虛擬機棧
      3. 1.1.3. 本地方法棧
      4. 1.1.4. Java堆(Java Heap)
      5. 1.1.5. 方法區
      6. 1.1.6. 運行時常量池
      7. 1.1.7. 直接內存
      8. </ol> </li>

      9. 1.2. HotSpot虛擬機

        1. 1.2.1. 對象的創建
        2. 1.2.2. 對象的訪問定位
        3. </ol> </li>

        4. 1.3. OOM異常的解決思路
        5. 1.4. 參考
        6. </ol> </li> </ol> </div>

          Java內存區域與內存溢出異常

          運行時數據區域

           深入理解jvm之內存區域與內存溢出

          程序計數器

          • 當前線程所執行的字節碼的行號指示器
          • 當前線程私有
          • 不會出現OutOfMemoryError情況
          • </ul>

            java虛擬機棧

            • 線程私有,生命周期與線程相同
            • java方法執行的內存模型,每個方法執行的同時都會創建一個棧幀,存儲局部變量表(基本類型、對象引用)、操作數棧、動態鏈接、方法出口等信息
            • StackOverflowError異常:當線程請求的棧深度大于虛擬機所允許的深度
            • OutOfMemoryError異常:如果棧的擴展時無法申請到足夠的內存
            • </ul>

              本地方法棧

              與虛擬機棧相似,主要為虛擬機使用到的Native方法服務,在HotSpot虛擬機中直接把本地方法棧與虛擬機棧二合一

              Java堆(Java Heap)

              java堆是被所有線程共享的一塊內存區域,在 虛擬機啟動時創建。此區域的唯一目的就是存儲對象實例。java堆是垃圾收集器管理的主要區域。java堆還可以細分為:新生代與老年代。在細一點有 Eden空間、Form Survivor空間、To Survivor空間等。

              • 可以通過-Xmx和-Xms控制堆的大小
              • OutOfMemoryError異常:當在堆中沒有內存完成實例分配,且堆也無法再擴展時。
              • </ul>

                方法區

                • 線程間共享
                • 用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據
                • OutOfMemoryError異常:當方法區無法滿足內存的分配需求時
                • </ul>

                  運行時常量池

                  • 方法區的一部分
                  • 用于存放編譯期生成的各種字面量與符號引用
                  • OutOfMemoryError異常:當常量池無法再申請到內存時
                  • </ul>

                    直接內存

                    • NIO可以使用Native函數庫直接分配堆外內存,堆中的DirectByteBuffer對象作為這塊內存的引用進行操作
                    • 大小不受Java堆大小的限制,受本機(服務器)內存限制
                    • OutOfMemoryError異常:系統內存不足時
                    • </ul>

                      HotSpot虛擬機

                      對象的創建

                      虛擬機遇到一條new指令時,首先將去檢查這個對象的參數是否在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,必須先執行類的加載過程。
                      在類加載檢查通過后,虛擬機將為新生對象分配內存。對象所需內存大小再類加載完成后便可確定。內存分配可以采用“指針碰撞”與“空閑列表”的方式。

                      對象的訪問定位

                      java程序需要通過棧上的reference數據來操作堆上的具體對象。訪問方式有使用句柄和直接指針兩種。

                      • 句柄訪問 java堆中將會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息
                      • 直接指針訪問 java堆對象的布局中必須考慮如何放置訪問類型數據的相關信息,reference中存儲的就是對象地址
                      • </ul>

                        OOM異常的解決思路

                        生成Dump快照文件:

                        • 通過jvm參數—XX:-HeapDumpOnOutOfMemoryError可以讓JVM在出現內存溢出是Dump出當前的內存轉儲快照
                        • 用jmap生產dump文件,win通過任務管理器查看tomcat的進程pid,linux用ps命令查看進程pid,然后用jmap命令
                        • </ul>

                          先通過內存映像分析工具(如Eclipse的Memory Analyzer)進行分析,常見的情況有:

                          • 內存泄露,對象已經死了,無法通過垃圾收集器進行自動回收,通過找出泄露的代碼位置和原因,才好確定解決方案;
                          • 內存溢出,內存中的對象都還必須存活著,這說明Java堆分配空間不足,檢查堆設置大小(-Xmx與-Xms),檢查代碼是否存在對象生命周期太長、持有狀態時間過長的情況。
                          • </ul> OOM異常示例:
                            package oom;

                            import java.util.ArrayList; import java.util.List;

                            /**

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!