憨人筆記之JVM-運行時數據區(方法區)

話很少說,幹就完了。java

方法區

方法區同Java堆同樣,也是線程共享區域。方法區主要存儲已經被虛擬機加載的類信息(包括類的名稱、方法信息、字段信息)、常量、靜態變量、即時編譯器編譯後的代碼(好比spring 使用IOC或者AOP建立bean時,或者使用cglib,反射的形式動態生成class信息等)等數據。spring

不少人又把方法區稱爲"永久代",其實這個說法並不等價。僅僅是由於HotSpot虛擬機選擇把GC分代收集拓展到方法區,或者說是使用永久代來實現方法區而已。對於其餘的虛擬機來講是不存在永久代的概念的。線程

方法區除了和堆同樣能夠不須要連續的內存,也能夠選擇固定的大小或者動態拓展,還可以選擇是否實現垃圾收集。垃圾收集相對來講在方法區會比較少的出現。較少的出現並不意味着不會出現垃圾收集內存回收的狀況,方法區的垃圾收集主要是針對常量池的回收和類型的寫在。指針

若方法區沒法知足內存分配需求時,會拋出OOM異常。cdn

運行時常量池

運行時常量池是屬於方法區的一部分,一般存儲的是在編譯期所產生的字面量及符號引用,以下圖所示:對象

在類加載以後,會將這部份內容由class常量池保存到運行時常量池中。

在說到類加載子系統時,類的加載由加載、連接(驗證、準備、解析)、初始化、使用等這麼幾個階段,而在類加載階段,主要作了如下幾件事情:blog

  1. 經過類的全限定名獲取二進制字節碼文件(class文件)
  2. 將類文件中的靜態存儲結構加載到運行時數據區中
  3. 在內存中生成java.class.Class對象,做爲方法區這個類的各類數據訪問的入口

可是須要特別說明的是,類加載階段所產生的類對象實例對象本質上是有區別的。類對象僅僅是在類加載的時候生成到內存中的對象,而實例對象通常是經過new關鍵字所建立的對象。它們的內存存儲區域也不同。在上述步驟2中,所謂靜態存儲結構加載到運行時數據區中其實際上就是將class常量池中的內容保存到運行時常量池中。內存

常量池中還保存這符號引用,在類的解析階段,會將這些符號引用轉換成直接引用(直接指向對象實例的指針地址)保存到常量池中。因此,在整個類加載階段,有兩個環節會將數據保存至常量池中。字符串

  • 加載:將class常量池中的數據保存到運行時常量池中。
  • 解析:將一部分符號引用轉換成直接引用保存到運行時常量池中。(ext:一般是在編譯期就肯定了調用版本的方法,例如類實例方法、私有方法、靜態方法和父類方法等)

運行時常量池相對於類常量池另外一個重要的特徵就是具有動態性,Java並不要求常量必定要在編譯器就產生,也就是說並非必定要在class常量池中的內容纔可以進入到運行時常量池,在程序運行期間,也可能將常量放入到常量池中。例如String類的intern()方法。編譯器

常量池的優勢

常量池屬於方法區的一部分那麼也是線程共享的,既然內存區域是共享的,那麼某種程度上就減輕了新對象頻繁建立銷燬的內存開銷,實現對象的共享。

例如字符串常量池:把全部字符串常量放到一個常量池中

  1. 節省內存空間:常量池中全部同樣的字符串常量會被合併至佔用一個內存空間
  2. 節省運行時間:比較字符串時,==比equals()快。對於兩個引用變量,只用==判斷引用是否相等,也就能夠判斷實際值是否相等。

運行時常量池屬於方法區的一部分,因此同方法區同樣,當出現內存不足時,也會出現OOM異常。


不怕路歹行不怕大雨淋,心上一字敢 面對個人夢,甘願來做憨人。 --<憨人>

相關文章
相關標籤/搜索