JVM元數據區

以前咱們已經講述過Java棧和堆,今天咱們看一下JVM另外一塊重要的內存區域,那就是元數據區。讀者若是對JVM的其餘文章感興趣的話能夠經過上方專輯進入查看JVM系列文章

元數據區

元數據區的概念出如今Java8之後,在Java8之前成爲方法區,元數據區也是一塊線程共享的內存區域,主要用來保存被虛擬機加載的類信息、常量、靜態變量以及即時編譯器編譯後的代碼等數據。java

因爲元數據存儲的信息不容易變更,所以它被安置在一塊堆外內存,大小由-XX:MaxMetaspaceSize指定。web

public class MetaSpaceTest {
public static void main(String[] args) { int i = 0; try { for (i = 0; i < 100000; i++) { new CglibBean(new HashMap<>()); } } catch (Exception e) { System.out.println(e.getMessage()); } finally { System.out.println("total create count:" + i); } }
public static class CglibBean {
public CglibBean(Object object) { Enhancer enhancer = new Enhancer(); enhancer.setUseCache(false); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> obj); enhancer.setSuperclass(object.getClass()); enhancer.create(); } }}

上述代碼經過Cglib生成大量的HashMap代理,下面咱們在運行這段代碼的時候指定下列參數shell

-XX:MaxMetaspaceSize=100M -XX:+PrintGCDetails


當咱們程序循環至3660次,也就是說咱們大約在生成了約3660個代理類之後元數據區發生了內存溢出,下面將MaxMetaspaceSize改成50M執行,微信


從上圖能夠看出當咱們生成了1710個代理類之後元數據區發生了內存溢出,可見一個元數據區的大小決定了Java虛擬機能夠裝載的類的多少。編輯器

運行時常量池

在元數據區中還有一塊區域稱爲運行時常量池,此區域用來程序運行期間產生的常量,以及編譯期生成的各類字面量和符號引用經類加載後的內容。flex

在Java中大概存儲三種常量池概念,下面咱們來說一下Java中其餘兩種常量池,幫助讀者瞭解他們中的區別。優化

首先你們在理解常量池的時候不要簡單的理解爲被final修飾的變量,常量在這裏的含義是一切不變的東西,包括final修飾的變量、字面量、類和接口全限定名、字段、方法名稱以及修飾符等永恆不變的東西spa

類文件常量池

類文件常量池是指.class文件中的Constant_Pool項,以下圖,類文件常量池存放的都是一些字面量和符號引用。.net

並非全部的字面量都會存儲在類文件常量池中,好比對於方法內(注意是方法)整數字面量,若是值在-32768~32767之間則會被直接嵌入JVM指令中去,不會保存在常量池中。因此讀者不會在常量池中知道CONSTANT_Integer_info爲1的符號引用。線程

類文件常量池產生於編譯時期,當JVM加載類文件時會將類文件常量池中的符合引用替換直接引用,加載以後的類文件信息將會被存放在運行時常量池。

字符串池

字符串池存在JDK1.6之前是存放在永久區中,可是在JDK1.7之後就被轉移到堆上。

public static void intern() { String s1 = new String("he") + new String("llo"); String s2 = s1.intern(); System.out.println(s1 == s2);}

上述代碼在JDK1.6的時候將會建立6個對象,首先new String("he")會在堆上創一個對象,而且"he"字面量會在永久區的字符串池上建立一個對象,new String("llo")同理建立了兩個對象,最後的+又建立了一個對象,當調用intern()方法時,首先會去查找字符串池查找是否有hello內容的對象,發現沒有則會在永久區中再建立一個對象,所以總共有6個對象,因爲s1是堆中的對象,s2是永久區字符串池中的對象,所以s1==s2結果爲false,詳情以下圖


可是在JDK1.6之後效果再也不如此,緣由就是因爲字符串常量池被移到了堆中,intern方法也作了優化,在JDK1.6之後上述代碼將會建立5個對象,首先new String("he")會在堆上創一個對象,而且"he"字面量也會在堆上建立一個對象, new String("llo")同理建立了兩個對象,最後的+又建立了一個對象。當intern調用時,首先會在字符串池中查找是否有hello內容的對象,發現沒有,此時不會主動建立而是先去查找堆中是否有hello內容的對象,若是有則直接將指針指向堆中的示例,所以這裏一共會建立5個對象,因爲s1和s2指向的是同一個對象實例,所以s1==s2爲true,詳情以下圖


問題

爲了幫助各位讀者真正理解字符串常量池,下面有兩段代碼,請在腦海中給出結果,而後再進行Coding驗證

public static void intern() { String s1 = new String("he") + new String("llo"); String s2 = s1.intern(); String s3 = "hello"; System.out.println(s1 == s3); System.out.println(s1 == s2);}
public static void intern() { String s3 = "hello"; String s1 = new String("he") + new String("llo"); String s2 = s1.intern(); System.out.println(s1 == s3); System.out.println(s1 == s2);}


本文分享自微信公衆號 - shysh95(shysh95)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索