一、JDK1.8 JVM記憶體模型概覽
這裡介紹的是JDK1.8 JVM記憶體模型。1.8同1.7比,最大的差別就是:中繼資料區取代了永久代。元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:中繼資料空間並不在虛擬機器中,而是使用本地記憶體。 二、各地區介紹 1. 程式計數器
每個線程一塊,指向當前線程正在執行的位元組碼代碼的行號。如果當前線程執行的是native方法,則其值為null。 2. Java虛擬機器棧
線程私人,每個線程對應一個Java虛擬機器棧,其生命週期與線程同進同退。每個Java方法在被調用的時候都會建立一個棧幀,併入棧。一旦完成調用,則出棧。所有的的棧幀都出棧後,線程也就完成了使命。 3. 本地方法棧
功能與Java虛擬機器棧十分相同。區別在於,本地方法棧為虛擬機器使用到的native方法服務。不多說。 4. 堆
堆是JVM記憶體佔用最大,管理最複雜的一個地區。其唯一的用途就是存放對象執行個體:所有的對象執行個體及數組都在對上進行分配。1.7後,字串常量池從永久代中剝離出來,存放在堆中。堆有自己進一步的記憶體分塊劃分,按照GC分代收集角度的劃分請參見上圖。 4.1 堆空間記憶體配置(預設情況下) 老年代 : 三分之二的堆空間 年輕代 : 三分之一的堆空間
eden區: 8/10 的年輕代空間 survivor0 : 1/10 的年輕代空間 survivor1 : 1/10 的年輕代空間
命令列上執行如下命令,查看所有預設的jvm參數
java -XX:+PrintFlagsFinal -version
輸出
輸出有大幾百行,這裡只取其中的兩個有關聯的參數
[Global flags] uintx InitialSurvivorRatio = 8 {product} uintx NewRatio = 2 {product} ... ...java version "1.8.0_91"Java(TM) SE Runtime Environment (build 1.8.0_91-b14)Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
參數解釋
| 參數 |
作用 |
| -XX:InitialSurvivorRatio |
新生代Eden/Survivor空間的初始比例 |
| -XX:Newratio |
Old區 和 Yong區 的記憶體比例 |
一道推算題
預設參數下,如果僅給出eden區40M,求堆空間總大小
根據比例可以推算出,兩個survivor區各5M,年輕代50M。老年代是年輕代的兩倍,即100M。那麼堆總大小就是150M。 4.2 字串常量池
JDK1.7 就開始“去永久代”的工作了。 1.7把字串常量池從永久代中剝離出來,存放在堆空間中。 a. jvm參數配置
-XX:MaxPermSize=10m-XX:PermSize=10m-Xms100m-Xmx100m-XX:-UseGCOverheadLimit
b. 測試代碼
public class StringOomMock { public static void main(String[] args) { try { List<String> list = new ArrayList<String>(); for (int i = 0; ; i++) { System.out.println(i); list.add(String.valueOf("String" + i++).intern()); } } catch (java.lang.Exception e) { e.printStackTrace(); } }}
c. jdk1.6 下的運行結果
jdk1.6 環境下是永久代OOM
153658153660Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at com.jd.im.StringOomMock.main(StringOomMock.java:17)
d. jdk1.7 下的運行結果
jdk1.7 下是堆OOM,並且伴隨著頻繁的FullGC, CPU一直高位運行
2252792225279422527962252798*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message can't create name string at ../../../src/share/instrument/JPLISAgent.c line: 807Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.nio.CharBuffer.wrap(CharBuffer.java:369) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) at java.io.PrintStream.write(PrintStream.java:526) at java.io.PrintStream.print(PrintStream.java:597) at java.io.PrintStream.println(PrintStream.java:736) at com.jd.im.StringOomMock.main(StringOomMock.java:16)
e. jdk1.8 下的運行結果
jdk1.8的運行結果同1.7的一樣,都是堆空間OOM。
223689822369002236902Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.lang.Integer.toString(Integer.java:403) at java.lang.String.valueOf(String.java:3099) at java.io.PrintStream.print(PrintStream.java:597) at java.io.PrintStream.println(PrintStream.java:736) at com.jd.im.StringOomMock.main(StringOomMock.java:16)
5. 中繼資料區
中繼資料區取代了1.7版本及以前的永久代。中繼資料區和永久代本質上都是方法區的實現。方法區存放虛擬機器載入的類資訊,靜態變數,常量等資料。
中繼資料區OOM測試: a. jvm參數配置
-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=50m
b. 測試代碼
藉助cglib架構產生新類。
public class MetaSpaceOomMock { public static void main(String[] args) { ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean(); while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MetaSpaceOomMock.class); enhancer.setCallbackTypes(new Class[]{Dispatcher.class, MethodInterceptor.class}); enhancer.setCallbackFilter(new CallbackFilter() { @Override public int accept(Method method) { return 1; } @Override public boolean equals(Object obj) { return super.equals(obj); } }); Class clazz = enhancer.createClass(); System.out.println(clazz.getName()); //顯示數量資訊(共載入過的類型數目,當前還有效類型數目,已經被卸載的類型數目) System.out.println("total: " + loadingBean.getTotalLoadedClassCount()); System.out.println("active: " + loadingBean.getLoadedClassCount()); System.out.println("unloaded: " + loadingBean.getUnloadedClassCount()); } }}
c. 運行輸出:
jvm.MetaSpaceOomMock$$EnhancerByCGLIB$$567f7ec0total: 6265active: 6265unloaded: 0jvm.MetaSpaceOomMock$$EnhancerByCGLIB$$3501581btotal: 6266active: 6266unloaded: 0Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345) at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492) at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93) at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91) at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480) at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:337) at jvm.MetaSpaceOomMock.main(MetaSpaceOomMock.java:38)Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:413) at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336) ... 12 moreCaused by: java.lang.OutOfMemoryError: Metaspace at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ... 17 more
如果是1.7的jdk,那麼報OOM的將是PermGen地區。 6. 直接記憶體
jdk1.4引入了NIO,它可以使用Native函數庫直接分配堆外記憶體。