一.堆辽剧、棧踩窖、方法區(qū)的交互關(guān)系
1.1 從線(xiàn)程共享與否角度
堆棧方法區(qū).png
【注】元空間是Java8及以后的叫法势就,Java7及之前是永久區(qū)
堆棧方法區(qū)二.png
二.方法區(qū)的理解
- 方法區(qū)看作是一塊獨(dú)立于Java堆的內(nèi)存空間
- 方法區(qū)與堆一樣杀饵,是多個(gè)線(xiàn)程共享的內(nèi)存區(qū)域
- 方法區(qū)在JVM啟動(dòng)時(shí)被創(chuàng)建底哥,關(guān)閉時(shí)釋放咙鞍,它的實(shí)際物理內(nèi)存和堆一樣都可以是不連續(xù)的
- 方法區(qū)的大小與堆一樣,可以固定大小或可擴(kuò)展
-
方法區(qū)的大小決定了系統(tǒng)可以保存多少個(gè)類(lèi)趾徽,定義太多類(lèi)续滋,方法區(qū)會(huì)溢出,JVM拋出內(nèi)存溢出錯(cuò)誤:
Java7及之前是java.lang.OutOfMemoryError:PerGen space附较,Java8及之后是java.lang.OutOfMemoryError:Metaspace【加載過(guò)多的第三方j(luò)ar包吃粒;Tomcat部署過(guò)多的工程咏窿;大量動(dòng)態(tài)生成反射類(lèi)】
方法區(qū)與永久代和元空間的關(guān)系
方法區(qū).png - 元空間的本質(zhì)與永久代類(lèi)似攻冷,都是方法區(qū)的實(shí)現(xiàn)涩咖。不過(guò)永久代使用的是JVM的內(nèi)存空間祟身,元空間使用本地內(nèi)存
三.設(shè)置方法區(qū)的大小與OOM
3.1 JDK7設(shè)置參數(shù)
【1】設(shè)置永久代初始化空間大小【默認(rèn)20.75M】
-XX:PermSize
【2】設(shè)置永久代最大空間【32位機(jī)默認(rèn)64M枯途,64位機(jī)默認(rèn)82M】
-XX:MaxPermSize
3.2 JDK8設(shè)置參數(shù)
【1】設(shè)置元空間初始化空間大小【默認(rèn)21M】
-XX:MetaspaceSize
【2】設(shè)置元空間最大空間【默認(rèn)-1豫尽,即沒(méi)有限制老翘,上限是本地內(nèi)存的大小】
-XX:MaxMetaspaceSize
3.3 方法區(qū)的OOM舉例
- **方法區(qū)主要存放的類(lèi)信息缅叠,所以可以生成大量的類(lèi)填滿(mǎn)方法區(qū)
四.方法區(qū)的內(nèi)部結(jié)構(gòu)
4.1 存放內(nèi)容
- 方法區(qū)存儲(chǔ)已被JVM加載的類(lèi)型信息卢鹦、常量臀脏、靜態(tài)變量【1.6及以前存放在永久代,1.7及以后存放在堆】冀自、JIT編譯后的代碼緩存
- 對(duì)每個(gè)加載的類(lèi)型【類(lèi)揉稚、接口、枚舉熬粗、注解】搀玖,JVM必須在方法區(qū)存儲(chǔ)以下信息,類(lèi)型的全名驻呐、父類(lèi)的全名【接口或java.lang.Object都沒(méi)有父類(lèi)】灌诅、類(lèi)型修飾符、這個(gè)類(lèi)型直接接口的一個(gè)有序列表
- 存儲(chǔ)域信息【Field也叫屬性】中域名稱(chēng)含末、修飾符猜拾、類(lèi)型
- 存儲(chǔ)方法信息中方法名稱(chēng)、返回類(lèi)型佣盒、修飾符挎袜、參數(shù)個(gè)數(shù)和類(lèi)型、方法的字節(jié)碼、操作數(shù)棧和局部變量表及大小【抽象和本地方法除外】宋雏、異常表【抽象和本地方法除外】
- 被修飾為fnal的類(lèi)變量【即 static final】芜飘,在編譯時(shí)就被分配了
4.2 運(yùn)行時(shí)常量池
4.2.1 運(yùn)行時(shí)常量池與常量池
- 【1】字節(jié)碼文件中有常量池,方法區(qū)中包含了運(yùn)行時(shí)常量池
- 【2】字節(jié)碼文件通過(guò)類(lèi)加載器加載到方法區(qū)磨总,就會(huì)創(chuàng)建對(duì)應(yīng)的運(yùn)行時(shí)常量池
4.2.2 常量池
- 【1】常量池中存儲(chǔ)的數(shù)據(jù)類(lèi)型:數(shù)量值嗦明、字符串值、類(lèi)引用 蚪燕、方法引用娶牌、字段引用
- 【2】常量池的作用:一個(gè)Java源文件編譯后產(chǎn)生字節(jié)碼文件,字節(jié)碼文件需要數(shù)據(jù)的支持馆纳,這些數(shù)據(jù)太大不能直接存放在字節(jié)碼中诗良,可以存儲(chǔ)到常量池,而字節(jié)碼文件中包含了指向常量池的引用 鲁驶,在動(dòng)態(tài)鏈接的時(shí)候會(huì)用到常量池鉴裹。
4.2.3 運(yùn)行時(shí)常量池
- 【1】運(yùn)行時(shí)常量池相較于class文件中的常量池,具有一個(gè)重要特性:具備動(dòng)態(tài)性
- 【2】運(yùn)行時(shí)常量池相較于class文件中的常量池钥弯,存儲(chǔ)的不再是符號(hào)引用径荔,而是直接引用,這個(gè)變化是在類(lèi)加載器子系統(tǒng)的第二個(gè)階段Lingking的解析步驟完成的
五.方法區(qū)使用舉例
六.方法區(qū)的演進(jìn)細(xì)節(jié)
- 6.1 HotSpot方法區(qū)的變化
版本 | 變化 |
---|---|
jdk1.6及以前 | 有永久代脆霎,靜態(tài)變量存儲(chǔ)在永久代上 |
jdk1.7 | 有永久代总处,但逐漸去永久代,字符串常量池睛蛛、靜態(tài)變量移除鹦马,存儲(chǔ)在堆中 |
jdk1.8及以后 | 無(wú)永久代,改為元空間忆肾,類(lèi)型信息【包括類(lèi)荸频、字段、方法】客冈、常量保存在本地內(nèi)存元空間试溯,但字符串常量池、靜態(tài)變量仍在堆中 |
JDK16.png
JDK17.png
JDK18.png
6.2 為什么永久代要被元空間替代【## http://openjdk.java.net/jeps/122】
【1】為永久代設(shè)置空間大小是很難確定的
在某些場(chǎng)景下郊酒,如果動(dòng)態(tài)加載的類(lèi)過(guò)多,容易產(chǎn)生Perm的OOM键袱。而元空間與永久代的最大區(qū)別是:元空間使用的是本地內(nèi)存燎窘,內(nèi)存大
【2】對(duì)永久代進(jìn)行調(diào)優(yōu)很困難,full GC蹄咖。方法區(qū)的垃圾回收主要回收兩部分褐健,常量池中廢棄的常量和不再使用的類(lèi)型6.3 StringTable為什么要調(diào)整
JDK1.7開(kāi)始將字符串常量池移動(dòng)到堆空間。老年代空間不足、永久代不足時(shí)才觸發(fā)full GC蚜迅。這就導(dǎo)致StringTable回收幾率低舵匾。而我們開(kāi)發(fā)中會(huì)創(chuàng)建大量的字符串,回收幾率低谁不,導(dǎo)致永久代空間不足坐梯,而放在堆中,能及時(shí)回收6.4 靜態(tài)變量存放在哪