天天看點

JVM學習筆記——方法區

  方法區在邏輯上屬于堆的一部分,但可以看做是一塊獨立于 Java 堆的記憶體空間。所有的字段和方法位元組碼,以及一些特殊的方法,如構造函數,接口代碼在此定義。所有定義方法的資訊都儲存在方法區。在 JDK 8 之後,方法區移動至本地記憶體中。

方法區具有以下特點:

方法區與 Java 堆一樣,是各個線程共享的記憶體區域

方法區在 JVM 啟動時建立完成,并且實際記憶體區域和 Java 堆區一樣都可以是不連續的

方法區的大小,跟堆空間大小一樣,可以選擇固定大小或者可擴充

方法區的大小決定了系統可以儲存多少類,在 JDK8 之後方法區溢出報錯改為 OutOfMemoryError:Metaspace

關閉 JVM 後方法區就被釋放

  靜态變量,常量,類資訊,運作時常量池存在方法區中。執行個體變量存在堆中,與方法區無關。

類型資訊

  對每個加載的類型(類class,接口interface,枚舉enum,注解annotation),在方法區存儲以下資訊:

類型的完整有效名稱,全限定名

類型直接父類的全限定名

類型的修飾符(public,abstract,final 的子集)

類型直接接口的一個有序表

域(Field)資訊(屬性、字段)

所有域的相關資訊以及域的聲明順序

域名稱,域類型,域修飾符(public,private,protected,static,final,volatile,transient 的子集)

方法資訊

聲明順序

方法名稱

方法的傳回值類型

方法參數的屬性,類型,順序

方法修飾符(public,private,protected,static,final,synchronized,native,abstract的子集)

  Java 中的常量池分為靜态常量池和運作時常量池。

靜态常量池

  即.class 檔案中的常量池,class 檔案中的常量池包含字元串(數字)字面量,類、方法的資訊,占用class檔案絕大部分空間。

運作時常量池

  JVM 虛拟機在完成類裝載後,将 class 檔案中的常量池載入到記憶體中,并儲存在方法區中,這就是運作時常量池

常量池表(Constant Pool Table)是 Class 檔案的一部分,用于存放編譯期間生成的各種字面量與符号引用,這部分内容将在類加載後存放到方法區的運作時常量池中

JDK 7 之前習慣吧方法區稱為永久代,JDK 8 後廢除永久代的概念,把方法區改為和 JRocket、J9 一樣的元空間(MetaSpace),并調整了方法區的内部結構,如将字元串常量由永久代轉移到堆中

元空間與永久代之間最大的差別在于:元空間并不在虛拟機中,而是使用本地記憶體。是以,預設情況下,元空間的大小僅受本地記憶體限制

  字元串常量由永久代轉移到堆中,是由于字元串存在永久代中,容易出現性能問題和記憶體溢出。

  類的靜态變量(class statics)轉移到了堆。類及方法的資訊等比較難确定其大小,是以對于永久代的大小指定比較困難,太小容易出現永久代溢出,太大則容易導緻老年代溢出。

永久代會為 GC 帶來不必要的複雜度,并且回收效率偏低。

JDK 8 之後,元空間可以使用

<code>-XX:MetaspaceSize</code>:設定初始空間大小,到達該值就會觸發垃圾收集,同時 GC 會對該值進行調整,windows 下,該值初始預設為 21M,無上限

<code>-XX:MetaspaceSize</code>:用于設定最大空間,無限制

<code>-XX:MinMetaspaceFreeRatio</code>:GC之後,最小的 Metaspace 剩餘空間容量的百分比,減少為配置設定空間所導緻的垃圾收集

<code>-XX:MaxMetaspaceFreeRatio</code>:GC之後,最大的 Metaspace 剩餘空間容量的百分比,減少為釋放空間所導緻的垃圾收集