您好,登錄后才能下訂單哦!
這篇文章主要講解了“什么是JVM元數(shù)據(jù)區(qū)”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“什么是JVM元數(shù)據(jù)區(qū)”吧!
元數(shù)據(jù)區(qū)的概念出現(xiàn)在Java8以后,在Java8以前成為方法區(qū),元數(shù)據(jù)區(qū)也是一塊線程共享的內(nèi)存區(qū)域,主要用來保存被虛擬機加載的類信息、常量、靜態(tài)變量以及即時編譯器編譯后的代碼等數(shù)據(jù)。
由于元數(shù)據(jù)存儲的信息不容易變動,因此它被安置在一塊堆外內(nèi)存,大小由-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代理,下面我們在運行這段代碼的時候指定下列參數(shù)
-XX:MaxMetaspaceSize=100M -XX:+PrintGCDetails
當我們程序循環(huán)至3660次,也就是說我們大約在生成了約3660個代理類以后元數(shù)據(jù)區(qū)發(fā)生了內(nèi)存溢出,下面將MaxMetaspaceSize改為50M執(zhí)行,
從上圖可以看出當我們生成了1710個代理類以后元數(shù)據(jù)區(qū)發(fā)生了內(nèi)存溢出,可見一個元數(shù)據(jù)區(qū)的大小決定了Java虛擬機可以裝載的類的多少。
在元數(shù)據(jù)區(qū)中還有一塊區(qū)域稱為運行時常量池,此區(qū)域用來程序運行期間產(chǎn)生的常量,以及編譯期生成的各種字面量和符號引用經(jīng)類加載后的內(nèi)容。
在Java中大概存儲三種常量池概念,下面我們來講一下Java中其他兩種常量池,幫助讀者了解他們中的區(qū)別。
首先大家在理解常量池的時候不要簡單的理解為被final修飾的變量,常量在這里的含義是一切不變的東西,包括final修飾的變量、字面量、類和接口全限定名、字段、方法名稱以及修飾符等永恒不變的東西。
類文件常量池是指.class文件中的Constant_Pool項,如下圖,類文件常量池存放的都是一些字面量和符號引用。
并不是所有的字面量都會存儲在類文件常量池中,比如對于方法內(nèi)(注意是方法)整數(shù)字面量,如果值在-32768~32767之間則會被直接嵌入JVM指令中去,不會保存在常量池中。所以讀者不會在常量池中知道CONSTANT_Integer_info為1的符號引用。
類文件常量池產(chǎn)生于編譯時期,當JVM加載類文件時會將類文件常量池中的符合引用替換直接引用,加載之后的類文件信息將會被存放在運行時常量池。
字符串池存在JDK1.6以前是存放在永久區(qū)中,但是在JDK1.7以后就被轉(zhuǎn)移到堆上。
public static void intern() { String s1 = new String("he") + new String("llo"); String s2 = s1.intern(); System.out.println(s1 == s2);}
上述代碼在JDK1.6的時候?qū)?chuàng)建6個對象,首先new String("he")會在堆上創(chuàng)一個對象,并且"he"字面量會在永久區(qū)的字符串池上創(chuàng)建一個對象,new String("llo")同理創(chuàng)建了兩個對象,最后的+又創(chuàng)建了一個對象,當調(diào)用intern()方法時,首先會去查找字符串池查找是否有hello內(nèi)容的對象,發(fā)現(xiàn)沒有則會在永久區(qū)中再創(chuàng)建一個對象,因此總共有6個對象,由于s1是堆中的對象,s2是永久區(qū)字符串池中的對象,因此s1==s2結(jié)果為false,詳情如下圖
但是在JDK1.6以后效果不再如此,原因就是由于字符串常量池被移到了堆中,intern方法也做了優(yōu)化,在JDK1.6以后上述代碼將會創(chuàng)建5個對象,首先new String("he")會在堆上創(chuàng)一個對象,并且"he"字面量也會在堆上創(chuàng)建一個對象, new String("llo")同理創(chuàng)建了兩個對象,最后的+又創(chuàng)建了一個對象。當intern調(diào)用時,首先會在字符串池中查找是否有hello內(nèi)容的對象,發(fā)現(xiàn)沒有,此時不會主動創(chuàng)建而是先去查找堆中是否有hello內(nèi)容的對象,如果有則直接將指針指向堆中的示例,因此這里一共會創(chuàng)建5個對象,由于s1和s2指向的是同一個對象實例,因此s1==s2為true,詳情如下圖
為了幫助各位讀者真正理解字符串常量池,下面有兩段代碼,請在腦海中給出結(jié)果,然后再進行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元數(shù)據(jù)區(qū)”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對什么是JVM元數(shù)據(jù)區(qū)這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。