JVM限制了Java程序的最大內(nèi)存, 修改/指定啟動(dòng)參數(shù)可以改變這種限制缅疟。Java將堆內(nèi)存劃分為多個(gè)部分, 如下圖所示:
【Java8及以上】這些內(nèi)存池的最大值, 由?-Xmx?和?-XX:MaxMetaspaceSize?等JVM啟動(dòng)參數(shù)指定. 如果沒有明確指定, 則根據(jù)平臺(tái)類型(OS版本+JVM版本)和物理內(nèi)存的大小來確定视搏。
java.lang.OutOfMemoryError: Metaspace?錯(cuò)誤所表達(dá)的信息是:?元數(shù)據(jù)區(qū)(Metaspace) 已被用滿
如果你是Java老司機(jī), 應(yīng)該對(duì) PermGen 比較熟悉. 但從Java 8開始,內(nèi)存結(jié)構(gòu)發(fā)生重大改變, 不再使用Permgen, 而是引入一個(gè)新的空間: Metaspace. 這種改變基于多方面的考慮, 部分原因列舉如下:
Permgen空間的具體多大很難預(yù)測。指定小了會(huì)造成?java.lang.OutOfMemoryError: Permgen size?錯(cuò)誤, 設(shè)置多了又造成浪費(fèi)姥芥。
為了?GC 性能?的提升, 使得垃圾收集過程中的并發(fā)階段不再?停頓, 另外對(duì) metadata 進(jìn)行特定的遍歷(specific iterators)。
對(duì)?G1垃圾收集器?的并發(fā) class unloading 進(jìn)行深度優(yōu)化。
在Java8中,將之前 PermGen 中的所有內(nèi)容, 都移到了 Metaspace 空間。例如: class 名稱, 字段, 方法, 字節(jié)碼, 常量池, JIT優(yōu)化代碼, 等等塞椎。
Metaspace 的使用量與JVM加載到內(nèi)存中的 class 數(shù)量/大小有關(guān)【Φ停可以說,?java.lang.OutOfMemoryError: Metaspace?錯(cuò)誤的主要原因, 是加載到內(nèi)存中的 class 數(shù)量太多或者體積太大案狠。
和?上一章的PermGen?類似, Metaspace 空間的使用量, 與JVM加載的 class 數(shù)量有很大關(guān)系。下面是一個(gè)簡單的示例:
publicclassMetaspace {staticjavassist.ClassPool cp = javassist.ClassPool.getDefault();publicstaticvoidmain(String[] args) throws Exception{for(inti =0; ; i++) {? ? ? Class c = cp.makeClass("eu.plumbr.demo.Generated"+ i).toClass();? ? }? }}
1
2
3
4
5
6
7
8
9
10
可以看到, 使用?javassist?工具庫生成 class 那是非常簡單钱雷。在 for 循環(huán)中, 動(dòng)態(tài)生成很多class, 最終將這些class加載到 Metaspace 中骂铁。
執(zhí)行這段代碼, 隨著生成的class越來越多, 最后將會(huì)占滿 Metaspace 空間, 拋出?java.lang.OutOfMemoryError: Metaspace. 在Mac OS X上, Java 1.8.0_05 環(huán)境下, 如果設(shè)置了啟動(dòng)參數(shù)?-XX:MaxMetaspaceSize=64m, 大約加載 70000 個(gè)class后JVM就會(huì)掛掉。
如果拋出與 Metaspace 有關(guān)的 OutOfMemoryError , 第一解決方案是增加 Metaspace 的大小. 使用下面這樣的啟動(dòng)參數(shù):
-XX:MaxMetaspaceSize=512m
1
這里將 Metaspace 的最大值設(shè)置為 512MB, 如果沒有用完, 就不會(huì)拋出?OutOfMemoryError急波。
有一種看起來很簡單的方案, 是直接去掉 Metaspace 的大小限制。 但需要注意, 不限制Metaspace內(nèi)存的大小, 假若物理內(nèi)存不足, 有可能會(huì)引起內(nèi)存交換(swapping), 嚴(yán)重拖累系統(tǒng)性能瘪校。 此外,還可能造成native內(nèi)存分配失敗等問題澄暮。
在現(xiàn)代應(yīng)用集群中,寧可讓應(yīng)用節(jié)點(diǎn)掛掉, 也不希望其響應(yīng)緩慢名段。
如果不想收到報(bào)警, 可以像鴕鳥一樣, 把?java.lang.OutOfMemoryError: Metaspace?錯(cuò)誤信息隱藏起來。 但這不能真正解決問題, 只會(huì)推遲問題爆發(fā)的時(shí)間泣懊。 如果確實(shí)存在內(nèi)存泄露, 請(qǐng)參考前面的文章, 認(rèn)真尋找解決方案伸辟。