【JVM】9、方法區

在這裏插入圖片描述

從線程共享與否的角度看運行時數據區結構圖:
ThreadLocal:如何保證多個線程在併發環境下的安全性?典型應用就是數據庫連接管理,以及獨立會話管理
線程共享->ThreadLocal->線程私有也是一個遞進的關係
在這裏插入圖片描述


棧 堆 方法區的交互關係

在這裏插入圖片描述

方法區的理解

官方文檔

  • 《Java虛擬機規範》中明確說明:儘管所有的方法區在邏輯上是屬於堆的一部分,但一些簡單的實現可能不會選擇去進行垃圾收集或者進行壓縮。
  • 但對於HotSpotJVM而言,方法區還有一個別名叫做Non-Heap(非堆),目的就是要和堆分開。
    方法區主要存放的是 Class,而堆中主要存放的是實例化的對象
  • 所以,方法區可以看作是一塊獨立於Java堆的內存空間。
  • 方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域
    多個線程同時加載統一個類時,只能有一個線程能加載該類,其他線程只能等等待該線程加載完畢,然後直接使用該類,即類只能加載一次。
  • 方法區在JVM啓動的時候被創建,並且它的實際的物理內存空間中和Java堆區一樣都可以是不連續的。
  • 方法區的大小,跟堆空間一樣,可以選擇固定大小或者可擴展。
  • 方法區的大小決定了系統可以保存多少個類,如果系統定義了太多的類,導致方法區溢出,虛擬機同樣會拋出內存
    • JDK7:java.lang.OutofMemoryError:PermGen space
    • JDK8:java.lang.OutOfMemoryError:Metaspace
      可能出現方法區OOM的情況:
      • 加載大量的第三方的jar包
      • Tomcat部署的工程過多(30~50個)
      • 大量動態的生成反射類

  • 關閉JVM就會釋放這個區域的內存

Hotspot 方法區的演進過程

  • 在 JDK7 及以前,習慣上把方法區,稱爲永久代。JDK8開始,使用元空間取代了永久代。
  • 根據《Java虛擬機規範》的規定,如果方法區無法滿足新的內存分配需求時,將拋出OOM異常
  • 永久代和元空間的區別:
    • 本質區別:永久代使用的是java虛擬機的內存(更容易出現OOM),元空間使用的是本地內存
    • 內部結構區別

在這裏插入圖片描述

在這裏插入圖片描述

設置方法區大小與OOM

jdk7永久代

JDK7 之前版本設置永久代大小

  • 通過-XX:Permsize來設置永久代初始分配空間。默認值是20.75M
  • -XX:MaxPermsize來設定永久代最大可分配空間。32位機器默認是64M,64位機器模式是82M
  • 當JVM加載的類信息容量超過了這個值,會報異常OutofMemoryError:PermGen space。
    在這裏插入圖片描述

JDK8 元空間

  • 元數據區大小可以使用參數 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 指定
  • 默認值依賴於平臺,Windows下,-XX:MetaspaceSize 約爲21M,-XX:MaxMetaspaceSize的值是-1,即沒有限制。
  • 與永久代不同,如果不指定大小,默認情況下,虛擬機會耗盡所有的可用系統內存。如果元數據區發生溢出,虛擬機一樣會拋出異常OutOfMemoryError:Metaspace
  • -XX:MetaspaceSize:設置初始的元空間大小。對於一個 64位 的服務器端 JVM 來說,其默認的 -XX:MetaspaceSize值爲21MB。這就是初始的高水位線,一旦觸及這個水位線,Full GC將會被觸發並卸載沒用的類(即這些類對應的類加載器不再存活),然後這個高水位線將會重置。新的高水位線的值取決於GC後釋放了多少元空間。
    • 如果釋放的空間不足,那麼在不超過MaxMetaspaceSize時,適當提高該值。
    • 如果釋放空間過多,則適當降低該值。
  • 如果初始化的高水位線設置過低,上述高水位線調整情況會發生很多次。通過垃圾回收器的日誌可以觀察到Full GC多次調用。爲了避免頻繁地GC,建議將-XX:MetaspaceSize設置爲一個相對較高的值。

方法區OOM

用反射的方式動態生成大量的類,這些類會加載到方法區,導致方法區OOM

如何解決OOM:

要解決OOM異常或heap space的異常,一般的手段是首先通過內存映像分析工具(如Ec1ipse Memory Analyzer)對dump出來的堆轉儲快照進行分析重點是確認內存中的對象是否是必要的,也就是要先分清楚到底是出現了內存泄漏(Memory Leak)還是內存溢出(Memory Overflow)
內存泄露:存在引用指向,但是那個數據也不會被使用了,但因爲有引用指向它所以他一直不會被回收

  • 是內存泄露
    找到那些內存泄露的對象,把引用斷掉
    進一步通過工具查看泄漏對象到GC Roots的引用鏈。於是就能找到泄漏對象是通過怎樣的路徑與GC Roots相關聯並導致垃圾收集器無法自動回收它們的。掌握了泄漏對象的類型信息,以及GC Roots引用鏈的信息,就可以比較準確地定位出泄漏代碼的位置。

  • 不存在內存泄露
    也就是純粹的OOM,單純的對象太多了
    如果不存在內存泄漏,換句話說就是內存中的對象確實都還必須存活着,那就應當檢查虛擬機的堆參數(-Xmx與-Xms),與機器物理內存對比看是否還可以調大,從代碼上檢查是否存在某些對象生命週期過長、持有狀態時間過長的情況,嘗試減少程序運行期的內存消耗。

方法區的內部結構

方法區存放的內容:
《深入理解Java虛擬機》書中對方法區(Method Area)存儲內容描述如下:它用於存儲已被虛擬機加載的類型信息、常量、靜態變量、即時編譯器編譯後的代碼緩存等。
方法區是記錄了他是被誰加載進來的

方法區中包括字節碼文件以及字節碼文件使用了哪一個加載器加載到方法區中的。即加載到方法區的類裏面記錄了它的classloader。 classloader加載到方法區也會記錄它都加載過誰。

類型信息

對每個加載的類型(類class、接口interface、枚舉enum、註解annotation),JVM必須在方法區中存儲以下類型信息:

  • 這個類型的完整有效名稱(全名=包名.類名)
  • 這個類型直接父類的完整有效名(對於interface或是java.lang.Object,都沒有父類)
  • 這個類型的修飾符(public,abstract,final的某個子集)
  • 這個類型直接接口的一個有序列表
    在這裏插入圖片描述

域(Field)信息

  • JVM必須在方法區中保存類型的所有域的相關信息以及域的聲明順序。
  • 域的相關信息包括:
    • 域名稱
    • 域類型
    • 域修飾符(public,private,protected,static,final,volatile,transient的某個子集)
      在這裏插入圖片描述

方法(Method)信息

JVM必須保存所有方法的以下信息,同域信息一樣包括聲明順序:

  • 方法名稱
  • 方法的返回類型(包括 void 返回類型),void 在 Java 中對應的類爲 void.class
  • 方法參數的數量和類型(按順序)
  • 方法的修飾符(public,private,protected,static,final,synchronized,native,abstract的一個子集)
  • 方法的字節碼(bytecodes)、操作數棧、局部變量表及大小(abstract和native方法除外)
  • 異常表(abstract和native方法除外),異常表記錄每個異常處理的開始位置、結束位置、代碼處理在程序計數器中的偏移地址、被捕獲的異常類的常量池索引
    在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章