自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JDK 方法區(qū)變遷史:版本間的差異與改進

開發(fā)
經??吹匠鯇WJVM的讀者會因為方法區(qū)這一概念提出一些混淆的問題和概念,所以這篇文章來幫助讀者梳理一下JVM中方法區(qū)的概念。

經??吹匠鯇WJVM的讀者會因為方法區(qū)這一概念提出下面這些混淆的問題和概念:

  • 什么是方法區(qū)?
  • 方法區(qū)和永久代還有元空間是什么關系?
  • JDK8版本的常量和靜態(tài)變量是在堆區(qū)?永久代?還是方法區(qū)?還是元空間?

所以,筆者這里就以這篇文章來幫助讀者梳理一下JVM中方法區(qū)的概念。

詳解各版本JVM方法區(qū)

方法區(qū)簡介

方法區(qū)其實是一個《Java虛擬機規(guī)范》一個邏輯上的概念,對于不同版本的JVM都有不同的實現(xiàn),就以我們常用的HotSpot JVM而言,方法區(qū)還有一個別名叫Non-Heap,即非堆內存,這么定義的目的自然是要讓Java開發(fā)者明白方法區(qū)和堆是一塊獨立于Java堆的內存空間,而這里筆者也列出方法區(qū)幾個通用的概念:

  • 方法區(qū)和Java堆內存一樣也是屬于各個線程共享的內存區(qū)域。
  • 方法區(qū)在JVM啟動就時創(chuàng)建,并且它實際的物理內存空間和Java堆內存一樣可以是不連續(xù)的,注意筆者所說,可以是不連續(xù)的。
  • 方法區(qū)內存大小也可以選擇固定大小或者可擴展。
  • 方法區(qū)的大小決定了系統(tǒng)可以保存多少個類,如果系統(tǒng)定義了太多的類,同樣會出現(xiàn)內存溢出的問題,可能是java.lang.OutOfMemoryError:PermGen space(永久代空間滿了),也可能是java.lang.OutOfMemoryError:Metaspace(元空間滿了),這一點筆者會在后文中方法區(qū)在各個版本中的實現(xiàn)進行拓展說明。

這里我們補充說明一下,后文所涉及的不同版本的JVM版本都是以HotSpot虛擬機展開探討。

JDK7之前的版本

先來在JDK7之前的版本內存結構圖,在這些版本上邏輯上方法區(qū)和堆區(qū)在邏輯上是連續(xù)的,實際上在物理內存上來說,它們卻可是一塊連續(xù)的內存。在JDK7之前的版本,它們都用的是一個名為PermGen(永久代)的虛作為方法區(qū)的實現(xiàn)。 這也是為什么很多讀者會把永久代和老年代混淆,實際上這兩個完全不是一個概念,在JDK7之前的版本,永久代僅僅是作為方法區(qū)的實現(xiàn)并和老年代捆綁在一起,當老年代或者永久代任何一個內存空間滿了的時候,都會觸發(fā)一次垃圾收集。

在這些個版本的JVM,方法區(qū)即永久代存儲的是:

  • 類信息
  • 字段信息
  • 方法信息
  • 常量
  • 靜態(tài)變量
  • 即時編譯器編譯后的代碼緩存等數(shù)據

JDK7版本的變化

JDK7則是基于原有的內存結構的基礎上將部分數(shù)據進行轉移:

  • 將符號引用(Symbols)轉移到Native Memory(本地內存),可能很多讀者經常聽到本地內存這一概念,這里筆者進行拓展解釋一下,本地內存即JVM運行時內存,它是不受GC管理的一塊內存區(qū)域,是直接由操作系統(tǒng)分配給JVM的一塊內存,需要程序手動進行獲取和釋放。
  • 因為永久代的GC是跟隨著老年代觸發(fā)的,所以考慮到垃圾回收的效率,JDK7將所有字符串常量的信息都直接移動到Java Heap中。
  • 類的靜態(tài)變量轉移到Java Heap中。

JDK8版本對于方法區(qū)的實現(xiàn)

最后我們再來說說現(xiàn)主流的JDK8版本,它基于JDK7的存儲方式,將永久代(Perm Gen) 改為元空間(Metaspace) 作為方法區(qū)的實現(xiàn),同時元空間不再與堆內存連續(xù),是一個劃分在本地內存(Native memory) 的一塊內存區(qū)域,這也就意味著JDK8版本實現(xiàn)的方法區(qū)不參與Java Heap的GC,僅僅處理元數(shù)據空間那些已卸載類的垃圾回收。

所以JDK8版本的內存結構最終如下圖所示,這也就意味著JDK7版本對永久代的設置參數(shù)(-XX:MaxPermSize) 變?yōu)闊o效參數(shù),取而代之的是對元空間空間大小設置的參數(shù)(-XX:MetaspaceSize)。

實踐驗證觀點

接下來我們通過幾段代碼來印證筆者的觀點,來看看這段代碼,筆者這里直接聲明了一段最大長度的靜態(tài)數(shù)組,這個數(shù)組長度為Integer.MAX_VALUE,粗略估算這個數(shù)組大致需要占用4G左右的內存空間。

//聲明一個靜態(tài)數(shù)組
 public static int[] arr=new int[Integer.MAX_VALUE];

    public void test(){
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

    public static void main(String[] args) {
       new Main().test();
    }

輸出結果如下,可以看到直接拋出了OOM異常,這也就意味著靜態(tài)變量在JDK8版本的堆內存中。

java.lang.OutOfMemoryError: Requested array size exceeds VM limit
 at com.sharkChili.webTemplate.Main.<clinit>(Main.java:16)
Exception in thread "main" 

同理的再來看看這段代碼。筆者聲明了一個常量數(shù)組,如果它也存在于堆內存中的話,那么它的運行結果也是OOM:

//常量全局數(shù)組
 final int[] arr = new int[Integer.MAX_VALUE];

    public void test() {
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

    public static void main(String[] args) {
        new Main().test();
    }

意料之內,在JDK8版本常量也是分配于堆內存中:

Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
 at com.sharkChili.webTemplate.Main.<init>(Main.java:15)
 at com.sharkChili.webTemplate.Main.main(Main.java:25)

接下來這個實驗比較特殊,我們都知道CGLIB是一個強大且高性能的字節(jié)碼生成庫,它支持運行時擴展Java類或接口實現(xiàn),本質上就是動態(tài)生成一個子類并覆蓋要代理的類。所以為了驗證JDK8版本的類信息是否是存于堆區(qū)還是方法區(qū),我們就基于一個CGLIB通過無限循環(huán)去創(chuàng)建無數(shù)的代理類,讓JVM去存儲這些類定義的信息,看看最終拋出的是OOM還是元空間不足。

為了能夠更快看到效果,筆者手動調整了一下元空間的大小:

-XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m

示例代碼如下,通過無限循環(huán)生成代理類并創(chuàng)建EmptyObject的代理對象:

public static void main(String[] args) {
       while (true){
           Enhancer enhancer = new Enhancer();
           //設置代理目標
           enhancer.setSuperclass(EmptyObject.class);
   //不生成同屬性類的靜態(tài)緩存
           enhancer.setUseCache(false);

           //設置單一回調對象,在調用中攔截對目標方法的調用
           enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(objects, args));
   //如有必要,生成一個新類,并使用指定的回調(如果有的話)創(chuàng)建一個新的對象實例。
           enhancer.create();
       }

    }

啟動后我們使用jvisualvm查看當前程序的GC情況,可以看到Java Heap運行正常,即時創(chuàng)建的無用代理對象都會被回收掉:

再來看看元空間,可以看到隨著實踐的推移,無數(shù)個全新的代理類的信息存到元空間,因為元空間不受GC管理,所以使用內存不斷增加:

最終如預期所說出現(xiàn)java.lang.OutOfMemoryError: Metaspace:

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
 at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
 at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
 at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
 at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
 at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
 at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
 at com.sharkChili.webTemplate.Main.main(Main.java:39)

常見面試題

1.為什么JDK8要將取消永久代的概念

大體來說取消永久代有以下兩個原因:

  • 首要原因是Hotspot和JRockit代碼合并,前者并沒有所謂的永久代。
  • 為了提高垃圾的回收的效率。我們都知道在JDK8版本之前老年代和永久代內存空間是連續(xù)的,任何一個滿了都可能觸發(fā)GC,這種做法對于永久代來說回收效率偏低(每次GC基本回收不了多少垃圾),且Hotspot為了做到這一點還需要專門對元數(shù)據信息進行特殊處理,所以為了簡化GC處理,JDK8版本就將方法區(qū)改為使用元空間實現(xiàn),如此后續(xù)對于元數(shù)據內存優(yōu)化可以專門處理而無需考慮對于堆空間的影響。

2.什么是方法區(qū)?是如何實現(xiàn)的?

方法區(qū)是Java虛擬機規(guī)范中定義的一塊用于存儲類信息、常量、靜態(tài)變量以及編譯器便后的代碼數(shù)據的邏輯內存區(qū)域,注意這里筆者所強調的是邏輯內存邏輯區(qū)域,而非物理形式的內存區(qū)域,而對應的內存實現(xiàn),在不同的JDK版本不同的實現(xiàn):

  • 在JDK6的版本方法區(qū)都是通過永久代進行實現(xiàn),存儲類信息、常量池(包括字符串常量池)、靜態(tài)變量和JIT編譯器編譯后的代碼等數(shù)據。
  • JDK7方法區(qū)還是永久代實現(xiàn),只不過將字符串常量池和靜態(tài)變量都存放到堆內存中,主要原因是永久代GC效率太低,只有在full gc的時候才會回收,所以將字符串常量池放到堆區(qū)保證高效的回收字符串。
  • 從JDK8開始方法區(qū)的實現(xiàn)直接用元空間來實現(xiàn),而元空間使用的即native memory,也就是本地內存,而本地內存即動態(tài)向操作系統(tǒng)獲取的內存空間,需要程序手動進行獲取和釋放,從Java的角度來說就是不受JVM虛擬機所約束的內存空間。也正是因為這幾個特點,保證元空間可以根據應用程序的需求動態(tài)調整大小,避免永久代內存溢出問題的同時還減少的GC回收的壓力。
責任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關推薦

2011-08-23 10:49:44

算法

2020-12-21 10:14:48

黑客網絡安全網絡攻擊

2024-01-26 08:33:14

JDK17JDK11版本

2010-07-30 08:30:38

VisualVMVisualVM 1.VisualVM 1.

2025-02-27 00:32:35

2014-12-31 17:16:15

知乎架構變遷史

2012-12-20 14:09:20

瀏覽器

2009-07-07 16:10:02

JDK最新版本JDK安裝JDK下載

2023-09-05 08:16:14

API架構

2011-07-18 09:34:51

2010-07-30 13:17:33

NFS V3

2011-04-12 14:26:04

電信間工作區(qū)綜合布線

2019-11-07 21:41:21

AndroidiOS不同

2011-07-29 18:03:30

IT職位變遷云計算

2020-09-20 17:50:38

編程語言PythonJava

2021-02-08 23:10:08

春晚紅包互聯(lián)網

2018-05-21 09:03:00

NASSAN案例

2009-08-18 22:15:38

VMware快照改進方

2011-11-03 15:25:07

Android

2023-03-13 00:10:46

Go語言版本
點贊
收藏

51CTO技術棧公眾號