您好,登錄后才能下訂單哦!
這篇文章主要講解了“什么是JVM元數據區”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“什么是JVM元數據區”吧!
元數據區的概念出現在Java8以后,在Java8以前成為方法區,元數據區也是一塊線程共享的內存區域,主要用來保存被虛擬機加載的類信息、常量、靜態變量以及即時編譯器編譯后的代碼等數據。
由于元數據存儲的信息不容易變動,因此它被安置在一塊堆外內存,大小由-XX:MaxMetaspaceSize指定。
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代理,下面我們在運行這段代碼的時候指定下列參數
-XX:MaxMetaspaceSize=100M -XX:+PrintGCDetails
當我們程序循環至3660次,也就是說我們大約在生成了約3660個代理類以后元數據區發生了內存溢出,下面將MaxMetaspaceSize改為50M執行,
從上圖可以看出當我們生成了1710個代理類以后元數據區發生了內存溢出,可見一個元數據區的大小決定了Java虛擬機可以裝載的類的多少。
在元數據區中還有一塊區域稱為運行時常量池,此區域用來程序運行期間產生的常量,以及編譯期生成的各種字面量和符號引用經類加載后的內容。
在Java中大概存儲三種常量池概念,下面我們來講一下Java中其他兩種常量池,幫助讀者了解他們中的區別。
首先大家在理解常量池的時候不要簡單的理解為被final修飾的變量,常量在這里的含義是一切不變的東西,包括final修飾的變量、字面量、類和接口全限定名、字段、方法名稱以及修飾符等永恒不變的東西。
類文件常量池是指.class文件中的Constant_Pool項,如下圖,類文件常量池存放的都是一些字面量和符號引用。
并不是所有的字面量都會存儲在類文件常量池中,比如對于方法內(注意是方法)整數字面量,如果值在-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);}
感謝各位的閱讀,以上就是“什么是JVM元數據區”的內容了,經過本文的學習后,相信大家對什么是JVM元數據區這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。