您好,登錄后才能下訂單哦!
深入淺析JVM中的參數分配?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
一、堆參數設置
-XX:+PrintGC 使用這個參數,虛擬機啟動后,只要遇到GC就會打印日志
-XX:+UseSerialGC 配置串行回收器
-XX:+PrintGCDetails 可以查看詳細信息,包括各個區的情況
-Xms:設置Java程序啟動時初始化堆大小
-Xmx:設置Java程序能獲得最大的堆大小
-Xmx20m -Xms5m -XX:+PrintCommandLineFlags:可以將隱式或者顯示傳給虛擬機的參數輸出
在實際工作中,我們可以直接將初始的堆大小與最大堆大小設置相等,這樣的好處是可以減少程序運行時的垃圾回收次數,從而提高性能。
配置運行時參數:-XX:+PrintGC -Xms5m -Xmx20m -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintCommandLineFlags
運行一下Demo:
package com.ietree.basicskill.jvm; public class Demo01 { public static void main(String[] args) { // -XX:+PrintGC -Xms5m -Xmx20m -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintCommandLineFlags //查看GC信息 System.out.println("max memory:" + Runtime.getRuntime().maxMemory()); System.out.println("free memory:" + Runtime.getRuntime().freeMemory()); System.out.println("total memory:" + Runtime.getRuntime().totalMemory()); byte[] b1 = new byte[1*1024*1024]; System.out.println("分配了1M"); System.out.println("max memory:" + Runtime.getRuntime().maxMemory()); System.out.println("free memory:" + Runtime.getRuntime().freeMemory()); System.out.println("total memory:" + Runtime.getRuntime().totalMemory()); byte[] b2 = new byte[4*1024*1024]; System.out.println("分配了4M"); System.out.println("max memory:" + Runtime.getRuntime().maxMemory()); System.out.println("free memory:" + Runtime.getRuntime().freeMemory()); System.out.println("total memory:" + Runtime.getRuntime().totalMemory()); } }
程序輸出:
-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC max memory:20316160 free memory:5286032 total memory:6094848 [GC (Allocation Failure) [DefNew: 789K->191K(1856K), 0.0026441 secs] 789K->530K(5952K), 0.0027627 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 分配了1M max memory:20316160 free memory:4469352 total memory:6094848 [GC (Allocation Failure) [DefNew: 1249K->0K(1856K), 0.0022285 secs][Tenured: 1554K->1554K(4096K), 0.0031394 secs] 1587K->1554K(5952K), [Metaspace: 2597K->2597K(1056768K)], 0.0054980 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 分配了4M max memory:20316160 free memory:4538184 total memory:10358784 Heap def new generation total 1920K, used 68K [0x00000000fec00000, 0x00000000fee10000, 0x00000000ff2a0000) eden space 1728K, 3% used [0x00000000fec00000, 0x00000000fec113e0, 0x00000000fedb0000) from space 192K, 0% used [0x00000000fedb0000, 0x00000000fedb0000, 0x00000000fede0000) to space 192K, 0% used [0x00000000fede0000, 0x00000000fede0000, 0x00000000fee10000) tenured generation total 8196K, used 5650K [0x00000000ff2a0000, 0x00000000ffaa1000, 0x0000000100000000) the space 8196K, 68% used [0x00000000ff2a0000, 0x00000000ff824888, 0x00000000ff824a00, 0x00000000ffaa1000) Metaspace used 2603K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
在此程序輸出的結果中,可以看到堆的詳細信息,比如可以看到它的新生代信息、老年代信息、永久區信息等。
二、新生代參數配置
-Xmn:可以設置新生代的大小,設置一個比較大的新生代會減少老年代的大小,這個參數對系統性能以及GC行為有很大的影響,新生代大小一般會設置整個堆空間的1/3到1/4左右。
-XX:SurvivorRatio:用來設置新生代中eden空間和from/to空間的比例。含義:-XX:SurvivorRatio=eden/from=eden/to。
不同的堆分布情況,對系統執行會產生一定的影響,在實際工作中,應該根據系統的特點做出合理的配置,基本策略:盡可能將對象預留在新生代,減少老年代的GC次數。
除了可以設置新生代的絕對大小(-Xmn),還可以使用(-XX:NewRatio)設置新生代和老年代的比例:-XX:NewRatio=老年代/新生代。
配置運行時參數:
-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails
-XX:+UseSerialGC
運行Demo:
package com.ietree.basicskill.jvm; public class Demo2 { public static void main(String[] args) { // 第一次配置(eden 2 = from 1 + to 1) // -Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC // 第二次配置 // -Xms20m -Xmx20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC // 第三次配置 // -XX:NewRatio=老年代/新生代 // -Xms20m -Xmx20m -XX:NewRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC byte[] b = null; // 連續向系統申請10MB空間 for (int i = 0; i < 10; i++) { b = new byte[1 * 1024 * 1024]; } } }
程序運行結果:
[GC (Allocation Failure) [DefNew: 508K->256K(768K), 0.0012770 secs] 508K->435K(20224K), 0.0013333 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 768K, used 498K [0x00000000fec00000, 0x00000000fed00000, 0x00000000fed00000) eden space 512K, 47% used [0x00000000fec00000, 0x00000000fec3c988, 0x00000000fec80000) from space 256K, 100% used [0x00000000fecc0000, 0x00000000fed00000, 0x00000000fed00000) to space 256K, 0% used [0x00000000fec80000, 0x00000000fec80000, 0x00000000fecc0000) tenured generation total 19456K, used 10419K [0x00000000fed00000, 0x0000000100000000, 0x0000000100000000) the space 19456K, 53% used [0x00000000fed00000, 0x00000000ff72cf20, 0x00000000ff72d000, 0x0000000100000000) Metaspace used 2601K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
可以看到,在新生代種,eden區域內存是from和to區域內存的2倍。即-XX:SurvivorRatio=2參數起了作用。
配置運行參數:-Xms20m -Xmx20m -XX:NewRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
運行以上Demo可以得到一下輸出:
[GC (Allocation Failure) [DefNew: 4979K->529K(6144K), 0.0028804 secs] 4979K->1553K(19840K), 0.0029572 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 5756K->0K(6144K), 0.0021035 secs] 6780K->2576K(19840K), 0.0021487 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 6144K, used 1134K [0x00000000fec00000, 0x00000000ff2a0000, 0x00000000ff2a0000) eden space 5504K, 20% used [0x00000000fec00000, 0x00000000fed1b9d8, 0x00000000ff160000) from space 640K, 0% used [0x00000000ff160000, 0x00000000ff160000, 0x00000000ff200000) to space 640K, 0% used [0x00000000ff200000, 0x00000000ff200000, 0x00000000ff2a0000) tenured generation total 13696K, used 2576K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000) the space 13696K, 18% used [0x00000000ff2a0000, 0x00000000ff524140, 0x00000000ff524200, 0x0000000100000000) Metaspace used 2601K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
發現tenured generation老年代的內存是new generation 新生代內存的2倍。
三、堆溢出參數配置
在Java程序的運行過程中,如果對空間不足,則會拋出內存溢出的錯誤(Out Of Memory)OOM,一旦這類問題發生在生產環境,則可能引起嚴重的業務中斷,Java虛擬機提供了-XX:+HeapDumpOnOutOfMemoryError,
使用該參數可以在內存溢出時導出整個堆信息,與之配合使用的還有參數-XX:HeapDumpPath,可以設置導出堆的存放路徑。
內存分析工具:Memory Analyzer
配置運行時參數-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Demo3.dump
運行Demo:
package com.ietree.basicskill.jvm; import java.util.Vector; public class Demo3 { public static void main(String[] args) { // -Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Demo3.dump // 堆內存溢出 Vector v = new Vector(); for (int i = 0; i < 5; i++) { v.add(new Byte[1 * 1024 * 1024]); } } }
程序輸出的結果:
java.lang.OutOfMemoryError: Java heap space Dumping heap to d:/Demo3.dump ... Heap dump file created [1219372 bytes in 0.009 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.ietree.basicskill.jvm.Demo3.main(Demo3.java:11)
在d:/Demo3.dump可以找到對應的文件,使用內存分析工具(Memory analyzer)打開:
如圖:
四、棧參數配置
Java虛擬機提供了參數-Xss來指定線程的最大棧空間,整個參數也直接決定了函數可調用的最大深度。
配置運行時參數:-Xss1m
運行Demo:
package com.ietree.basicskill.jvm; public class Demo4 { // -Xss1m // -Xss5m // 棧調用深度 private static int count; public static void recursion() { count++; recursion(); } public static void main(String[] args) { try { recursion(); } catch (Throwable t) { System.out.println("調用最大深入:" + count); t.printStackTrace(); } } }
程序輸出:
調用最大深入:20557 java.lang.StackOverflowError at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) at com.ietree.basicskill.jvm.Demo4.recursion(Demo4.java:12) ......
五、方法區參數配置
和Java堆一樣,方法區是一塊所有線程共享的內存區域,它用于保存系統的類信息,方法區(永久區)可以保存多少信息可以對其進行配置,在默認情況下,-XX:MaxPermSize為64M,
如果系統運行時生產大量的類,就需要設置一個相對合適的方法區,以免出現永久區內存溢出的問題。
-XX:PermSize=64M -XX:MaxPermSize=64M
六、直接內存參數配置
直接內存也是Java程序中非常重要的組成部分,特別是廣泛用在NIO中,直接內存跳過了Java堆,使用Java程序可以直接訪問原生堆空間,因此在一定程度上加快了內存空間的訪問速度。
但是說直接內存一定就可以提高內存訪問速度也不見得,具體情況具體分析。
相關配置參數:-XX:MaxDirectMemorySize,如果不設置,默認值為最大堆空間,即-Xmx。直接內存使用達到上限時,就會觸發垃圾回收,如果不能有效的釋放空間,就會引起系統的OOM。
七、對象進入老年代的參數配置
一般而言,對象首次創建會被放置在新生代的eden區,如果沒有GC介入,則對象不會離開eden區,那么eden區的對象如何進入老年代呢?
通常情況下,只要對象的年齡達到一定的大小,就會自動離開年輕代進入老年代,對象年齡是由對象經歷數次GC決定的,在新生代每次GC之后如果對象沒有被回收,則年齡加1。
虛擬機提供了一個參數來控制新生代對象的最大年齡,當超過這個年齡范圍就會晉升老年代。
-XX:MaxTenuringThreshold,默認情況下為15
配置運行時參數:-Xmx64M -Xms64M -XX:+PrintGCDetails
運行Demo:
package com.ietree.basicskill.jvm; public class Demo5 { public static void main(String[] args) { // 初始的對象在eden區 // 參數:-Xmx64M -Xms64M -XX:+PrintGCDetails for (int i = 0; i < 5; i++) { byte[] b = new byte[1024 * 1024]; } } }
程序輸出:
Heap PSYoungGen total 18944K, used 6759K [0x00000000feb00000, 0x0000000100000000, 0x0000000100000000) eden space 16384K, 41% used [0x00000000feb00000,0x00000000ff199db8,0x00000000ffb00000) from space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000) to space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000) ParOldGen total 44032K, used 0K [0x00000000fc000000, 0x00000000feb00000, 0x00000000feb00000) object space 44032K, 0% used [0x00000000fc000000,0x00000000fc000000,0x00000000feb00000) Metaspace used 2601K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
結論:對象首次創建會被放置在新生代的eden區,因此輸出結果中from和to區都為0%。
根據設置MaxTenuringThreshold參數,可以指定新生代對象經過多少次回收后進入老年代。另外,大對象新生代eden區無法裝入時,也會直接進入老年代。
JVM里有個參數可以設置對象的大小超過在指定的大小之后,直接晉升老年代。
-XX:PretenureSizeThreshold=15
參數:-Xmx1024M -Xms1024M -XX:+UseSerialGC -XX:MaxTenuringThreshold=15 -XX:+PrintGCDetails
使用PretenureSizeThreshold可以進行指定進入老年代的對象大小,但是要注意TLAB區域優先分配空間。虛擬機對于體積不大的對象 會優先把數據分配到TLAB區域中,因此就失去了在老年代分配的機會.
參數:-Xmx30M -Xms30M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:PretenureSizeThreshold=1000 -XX:-UseTLAB
八、TLAB參數配置
TLAB全稱是Thread Local Allocation Buffer即線程本地分配緩存,從名字上看是一個線程專用的內存分配區域,是為了加速對象分配對象而生的。
每一個線程都會產生一個TLAB,該線程獨享的工作區域,Java虛擬機使用這種TLAB區來避免多線程沖突問題,提高了對象分配的效率。
TLAB空間一般不會太大,當大對象無法在TLAB分配時,則會直接分配到堆上。
-XX:+UseTLAB使用TLAB
-XX:+TLABSize設置TLAB大小
-XX:TLABRefillWasteFraction設置維護進入TLAB空間的單個對象大小,它是一個比例值,默認為64,即如果對象大于整個空間的1/64,則在堆創建對象。
-XX:+PrintTLAB查看TLAB信息
-XX:ResizeTLAB自調整TLABRefillWasteFraction閾值。
參數:-XX:+UseTLAB -XX:+PrintTLAB -XX:+PrintGC -XX:TLABSize=102400 -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=100 -XX:-DoEscapeAnalysis -server
關于深入淺析JVM中的參數分配問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。