
1 來源
- 來源:《Java虛拟機 JVM故障診斷與性能優化》——葛一鳴
- 章節:第三章
本文是第三章的一些筆記整理。
2 GC
日志: -Xlog:gc
GC
-Xlog:gc
要列印
GC
日志的話,可以加上
-Xlog:gc
參數(
JDK8
及以下請使用
-XX:+PrintGC
),開啟
GC
列印後,每次
GC
就會列印如下的日志(
OpenJDK11 -Xlog:gc
):
[0.126s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 25M->0M(502M) 1.902ms
[0.205s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 300M->0M(502M) 4.174ms
[0.236s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 300M->0M(502M) 2.067ms
[0.268s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 300M->0M(502M) 2.362ms
其中開頭的時間表示發生
GC
的時刻,
25M->0M(502M)
表示:
-
前,堆使用量為GC
25M
-
後,堆使用量為GC
0M
- 堆空間總和約為
502M
末尾的時間表示本次
GC
的耗時。
另外如果需要更加詳細的參數,可以使用
-Xlog:gc*
(
JDK8
-XX:+PrintGCDetails
),比如下面是一部分的
GC
日志(
-Xlog:gc*
[0.137s][info][gc,start ] GC(0) Pause Young (Normal) (G1 Evacuation Pause)
[0.138s][info][gc,task ] GC(0) Using 10 workers of 10 for evacuation
[0.147s][info][gc,phases ] GC(0) Pre Evacuate Collection Set: 0.0ms
[0.147s][info][gc,phases ] GC(0) Evacuate Collection Set: 8.8ms
[0.147s][info][gc,phases ] GC(0) Post Evacuate Collection Set: 0.2ms
[0.147s][info][gc,phases ] GC(0) Other: 0.8ms
[0.147s][info][gc,heap ] GC(0) Eden regions: 25->0(300)
[0.147s][info][gc,heap ] GC(0) Survivor regions: 0->1(4)
[0.147s][info][gc,heap ] GC(0) Old regions: 0->0
[0.147s][info][gc,heap ] GC(0) Humongous regions: 0->0
[0.147s][info][gc,metaspace ] GC(0) Metaspace: 6633K->6633K(1056768K)
[0.147s][info][gc ] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 25M->0M(502M) 9.878ms
[0.147s][info][gc,cpu ] GC(0) User=0.05s Sys=0.00s Real=0.01s
- 行首的時間:事件發生的時刻
-
:這是第1次GC(0)
,接着會有GC
、GC(1)
GC(2)
-
:這次Pause Young(Normal)
回收了新生代GC
-
:使用10個工作線程Using 10 workers
-
/Pre Evacuate Collection Set
Evacuate Collection Set
Post Evacuate
:表示Other
垃圾回收标記,清除算法不同階段所花費的時間G1
-
:分别表示Eden/Survivor/Old/Humongous/Metaspace
eden區
存活區
老年區
(就是很大很大的對象所在的區域)、巨型對象區
在中繼資料區
前後的大小GC
-
:25M-0M(502M)
前堆占用GC
,25M
後為GC
,可用堆空間為0M
502M
-
User/Sys/Real
使用者态CPU耗時
系統CPU耗時
GC真實經曆時間
如果想檢視更全面的堆資訊,可以使用
Visual VM
,将在後續文章中叙述。
另外如果需要将日志持久化,可以使用
-Xlog:gc:gc.log
。
3 系統參數列印
參數
-XX:+PrintVMOptinos
可以列印運作時接收到的顯式參數,而
-XX:+PrintCommandLineFlags
可以列印傳遞給
JVM
的隐式與顯式參數:
另外一個參數是
-XX:+PrintFlagsFinal
,會列印所有系統參數的值(數量很多):
4 堆參數
4.1 最大堆與初始堆參數
Java
程序啟動時,虛拟機就會配置設定一塊初始堆空間,可以使用參數
-Xms
指定這塊空間的初始化大小。一般來說虛拟機會盡可能維持在初始堆空間範圍内運作,但是如果初始堆空間耗盡,虛拟機會将堆空間進行擴充,擴充上限為最大堆空間,最大堆空間可以使用參數
-Xmx
指定。
來一段代碼測試一下:
public class Main {
public static void main(String[] args){
printMemory();
byte [] bytes = new byte[1*1024*1024];
System.out.println("Allocate 1024 KB array");
printMemory();
bytes = new byte[4*1024*1024];
System.out.println("Allocate 4096 KB array");
printMemory();
}
public static void printMemory(){
System.out.println();
System.out.println("Max memory = " + Runtime.getRuntime().maxMemory() / 1024+ " KB");
System.out.println("Free memory = "+Runtime.getRuntime().freeMemory()/1024+ " KB");
System.out.println("Total memory = " + Runtime.getRuntime().totalMemory()/ 1024+ " KB");
System.out.println();
}
}
參數:
-Xmx20m
-Xms5m
-XX:+PrintCommandLineFlags
-Xlog:gc*
-XX:+UseSerialGC
輸出:
-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=20971520 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseSerialGC
[0.002s][info ][gc] Using Serial
[0.002s][info ][gc,heap,coops] Heap address: 0x00000000fec00000, size: 20 MB, Compressed Oops mode: 32-bit
[0.110s][info ][gc,start ] GC(0) Pause Young (Allocation Failure)
[0.112s][info ][gc,heap ] GC(0) DefNew: 1664K->192K(1856K)
[0.112s][info ][gc,heap ] GC(0) Tenured: 0K->598K(4096K)
[0.112s][info ][gc,metaspace ] GC(0) Metaspace: 6436K->6436K(1056768K)
[0.112s][info ][gc ] GC(0) Pause Young (Allocation Failure) 1M->0M(5M) 2.069ms
[0.112s][info ][gc,cpu ] GC(0) User=0.00s Sys=0.00s Real=0.01s
Max memory = 19840 KB
Free memory = 4797 KB
Total memory = 5952 KB
Allocate 1024 KB array
Max memory = 19840 KB
Free memory = 3773 KB
Total memory = 5952 KB
[0.128s][info ][gc,start ] GC(1) Pause Young (Allocation Failure)
[0.129s][info ][gc,start ] GC(2) Pause Full (Allocation Failure)
[0.129s][info ][gc,phases,start] GC(2) Phase 1: Mark live objects
[0.130s][info ][gc,phases ] GC(2) Phase 1: Mark live objects 1.366ms
[0.130s][info ][gc,phases,start] GC(2) Phase 2: Compute new object addresses
[0.130s][info ][gc,phases ] GC(2) Phase 2: Compute new object addresses 0.235ms
[0.130s][info ][gc,phases,start] GC(2) Phase 3: Adjust pointers
[0.131s][info ][gc,phases ] GC(2) Phase 3: Adjust pointers 0.624ms
[0.131s][info ][gc,phases,start] GC(2) Phase 4: Move objects
[0.131s][info ][gc,phases ] GC(2) Phase 4: Move objects 0.042ms
[0.131s][info ][gc ] GC(2) Pause Full (Allocation Failure) 1M->1M(5M) 2.335ms
[0.131s][info ][gc,heap ] GC(1) DefNew: 1579K->0K(1856K)
[0.131s][info ][gc,heap ] GC(1) Tenured: 598K->1899K(4096K)
[0.131s][info ][gc,metaspace ] GC(1) Metaspace: 6624K->6624K(1056768K)
[0.131s][info ][gc ] GC(1) Pause Young (Allocation Failure) 2M->1M(5M) 3.636ms
[0.131s][info ][gc,cpu ] GC(1) User=0.00s Sys=0.01s Real=0.00s
Allocate 4096 KB array
Max memory = 19840 KB
Free memory = 4087 KB
Total memory = 10116 KB
[0.133s][info ][gc,heap,exit ] Heap
[0.133s][info ][gc,heap,exit ] def new generation total 1920K, used 44K [0x00000000fec00000, 0x00000000fee10000, 0x00000000ff2a0000)
[0.133s][info ][gc,heap,exit ] eden space 1728K, 2% used [0x00000000fec00000, 0x00000000fec0b198, 0x00000000fedb0000)
[0.133s][info ][gc,heap,exit ] from space 192K, 0% used [0x00000000fedb0000, 0x00000000fedb0000, 0x00000000fede0000)
[0.133s][info ][gc,heap,exit ] to space 192K, 0% used [0x00000000fede0000, 0x00000000fede0000, 0x00000000fee10000)
[0.133s][info ][gc,heap,exit ] tenured generation total 8196K, used 5995K [0x00000000ff2a0000, 0x00000000ffaa1000, 0x0000000100000000)
[0.133s][info ][gc,heap,exit ] the space 8196K, 73% used [0x00000000ff2a0000, 0x00000000ff87aed0, 0x00000000ff87b000, 0x00000000ffaa1000)
[0.133s][info ][gc,heap,exit ] Metaspace used 6640K, capacity 6723K, committed 7040K, reserved 1056768K
[0.133s][info ][gc,heap,exit ] class space used 590K, capacity 623K, committed 640K, reserved 1048576K
最大記憶體由
-XX:MaxHeapSize
指定,該值為
-Xmx
的值,也就是
20 * 1024 * 1024
,而列印的最大可用記憶體為
20316160
,比設定的值少,這是因為配置設定給堆的記憶體空間與實際可用的記憶體空間并不是同一個概念,由于
GC
的需要,虛拟機會對堆空間進行分區管理,不同的區會采用不同的回收算法,一些算法會使用空間換時間的政策,是以會存在損失,最終的結果是實際可用記憶體會浪費大小等于
from
to
的空間,從輸出可以知道:
[0.139s][info][gc,heap,exit ] from space 192K, 0% used [0x00000000fedb0000, 0x00000000fedb0000, 0x00000000fede0000)
from
的大小為
192k
,但是實際情況是最大可用記憶體
19840k
+
from
的
192k
=
20032k
,并不是配置設定的記憶體
20480k
,這是因為虛拟機會對
from
to
進行對齊,将最大可用記憶體加上對齊後的
from
to
即得到配置設定的記憶體大小。
另外,列印顯示,初始運作空閑記憶體
4797k
,配置設定一個
1024k
數組後,空閑記憶體為
3773k
,正好符合,接着配置設定
4096k
,因為記憶體不足,對堆空間進行擴充後再配置設定,擴充後的堆大小為
10116k
在實際工作中,可以将
-Xms
與
-Xmx
設定為相同,這樣可以減少運作時的
GC
次數,提高性能。
4.2 新生代參數
-Xmn
可以設定新生代的大小,設定一個較大的新生代會減小老年代的大小,這個參數堆
GC
有很大影響,一般設定為堆空間的
1/3-1/4
。參數
-XX:SurvivorRatio
,也就是幸存區比例,可用來設定
eden區
from/to區
的比例,相當于:
-XX:SurvivorRatio=eden/from=eden/to
一個簡單的例子如下:
public static void main(String[] args){
byte [] b = null;
for (int i = 0; i < 10; i++) {
b = new byte[1*1024*1024] ;
}
}
-Xmx20m
-Xms20m
-Xmn1m
-XX:SurvivorRatio=2
-Xlog:gc*
-XX:+UseSerialGC
[0.002s][info][gc] Using Serial
[0.002s][info][gc,heap,coops] Heap address: 0x00000000fec00000, size: 20 MB, Compressed Oops mode: 32-bit
[0.042s][info][gc,start ] GC(0) Pause Young (Allocation Failure)
[0.044s][info][gc,heap ] GC(0) DefNew: 512K->256K(768K)
[0.044s][info][gc,heap ] GC(0) Tenured: 0K->172K(19456K)
[0.044s][info][gc,metaspace ] GC(0) Metaspace: 3871K->3871K(1056768K)
[0.044s][info][gc ] GC(0) Pause Young (Allocation Failure) 0M->0M(19M) 1.617ms
[0.044s][info][gc,cpu ] GC(0) User=0.01s Sys=0.00s Real=0.00s
[0.064s][info][gc,start ] GC(1) Pause Young (Allocation Failure)
[0.065s][info][gc,heap ] GC(1) DefNew: 767K->76K(768K)
[0.065s][info][gc,heap ] GC(1) Tenured: 172K->425K(19456K)
[0.065s][info][gc,metaspace ] GC(1) Metaspace: 4518K->4518K(1056768K)
[0.065s][info][gc ] GC(1) Pause Young (Allocation Failure) 0M->0M(19M) 0.870ms
[0.065s][info][gc,cpu ] GC(1) User=0.00s Sys=0.00s Real=0.00s
[0.093s][info][gc,heap,exit ] Heap
[0.093s][info][gc,heap,exit ] def new generation total 768K, used 562K [0x00000000fec00000, 0x00000000fed00000, 0x00000000fed00000)
[0.093s][info][gc,heap,exit ] eden space 512K, 94% used [0x00000000fec00000, 0x00000000fec79730, 0x00000000fec80000)
[0.093s][info][gc,heap,exit ] from space 256K, 29% used [0x00000000fec80000, 0x00000000fec93260, 0x00000000fecc0000)
[0.093s][info][gc,heap,exit ] to space 256K, 0% used [0x00000000fecc0000, 0x00000000fecc0000, 0x00000000fed00000)
[0.093s][info][gc,heap,exit ] tenured generation total 19456K, used 10665K [0x00000000fed00000, 0x0000000100000000, 0x0000000100000000)
[0.093s][info][gc,heap,exit ] the space 19456K, 54% used [0x00000000fed00000, 0x00000000ff76a630, 0x00000000ff76a800, 0x0000000100000000)
[0.093s][info][gc,heap,exit ] Metaspace used 6190K, capacity 6251K, committed 6528K, reserved 1056768K
[0.093s][info][gc,heap,exit ] class space used 535K, capacity 570K, committed 640K, reserved 1048576K
eden區
from區
的比值為
2:1
,是以
eden區
為
512K
,總可用新生代大小為
512K+256K=768K
,新生代總大小為
512K+256K+256K=1M
,由于
eden
區無法容納配置設定
1MB
數組,是以觸發了新生代
GC
,所有數組配置設定在了老年代。
而如果使用
-Xmn7m
(其他參數保持不變),輸出如下:
[0.003s][info][gc] Using Serial
[0.003s][info][gc,heap,coops] Heap address: 0x00000000fec00000, size: 20 MB, Compressed Oops mode: 32-bit
[0.096s][info][gc,start ] GC(0) Pause Young (Allocation Failure)
[0.097s][info][gc,heap ] GC(0) DefNew: 2684K->1752K(5376K)
[0.097s][info][gc,heap ] GC(0) Tenured: 0K->0K(13312K)
[0.097s][info][gc,metaspace ] GC(0) Metaspace: 5929K->5929K(1056768K)
[0.097s][info][gc ] GC(0) Pause Young (Allocation Failure) 2M->1M(18M) 1.350ms
[0.097s][info][gc,cpu ] GC(0) User=0.00s Sys=0.00s Real=0.00s
[0.098s][info][gc,start ] GC(1) Pause Young (Allocation Failure)
[0.099s][info][gc,heap ] GC(1) DefNew: 4928K->1024K(5376K)
[0.099s][info][gc,heap ] GC(1) Tenured: 0K->727K(13312K)
[0.099s][info][gc,metaspace ] GC(1) Metaspace: 5996K->5996K(1056768K)
[0.099s][info][gc ] GC(1) Pause Young (Allocation Failure) 4M->1M(18M) 1.142ms
[0.099s][info][gc,cpu ] GC(1) User=0.01s Sys=0.00s Real=0.00s
[0.100s][info][gc,start ] GC(2) Pause Young (Allocation Failure)
[0.100s][info][gc,heap ] GC(2) DefNew: 4180K->1024K(5376K)
[0.100s][info][gc,heap ] GC(2) Tenured: 727K->728K(13312K)
[0.100s][info][gc,metaspace ] GC(2) Metaspace: 6008K->6008K(1056768K)
[0.100s][info][gc ] GC(2) Pause Young (Allocation Failure) 4M->1M(18M) 0.190ms
[0.100s][info][gc,cpu ] GC(2) User=0.00s Sys=0.00s Real=0.00s
[0.100s][info][gc,heap,exit ] Heap
[0.100s][info][gc,heap,exit ] def new generation total 5376K, used 4211K [0x00000000fec00000, 0x00000000ff300000, 0x00000000ff300000)
[0.100s][info][gc,heap,exit ] eden space 3584K, 88% used [0x00000000fec00000, 0x00000000fef1cc00, 0x00000000fef80000)
[0.100s][info][gc,heap,exit ] from space 1792K, 57% used [0x00000000ff140000, 0x00000000ff2402a0, 0x00000000ff300000)
[0.100s][info][gc,heap,exit ] to space 1792K, 0% used [0x00000000fef80000, 0x00000000fef80000, 0x00000000ff140000)
[0.100s][info][gc,heap,exit ] tenured generation total 13312K, used 728K [0x00000000ff300000, 0x0000000100000000, 0x0000000100000000)
[0.100s][info][gc,heap,exit ] the space 13312K, 5% used [0x00000000ff300000, 0x00000000ff3b61f8, 0x00000000ff3b6200, 0x0000000100000000)
[0.100s][info][gc,heap,exit ] Metaspace used 6034K, capacity 6091K, committed 6272K, reserved 1056768K
[0.100s][info][gc,heap,exit ] class space used 518K, capacity 538K, committed 640K, reserved 1048576K
此參數下,
eden區
有足夠的空間,所有數組配置設定在
eden
區,但是不足以預留
10M
空間,是以産生了
GC
,每次申請空間也廢棄了上一次申請的空間,在新生代
GC
中有效回收了這些記憶體,最後的結果是所有記憶體配置設定都在新生代進行,隻是在
GC
過程中部分新生代對象晉升到了老年代。
再次增大新生代,使用
-Xmn15m -XX:SurvivorRatio=8
(其他參數不變),輸出如下:
[0.003s][info][gc] Using Serial
[0.003s][info][gc,heap,coops] Heap address: 0x00000000fec00000, size: 20 MB, Compressed Oops mode: 32-bit
start
[0.097s][info][gc,start ] GC(0) Pause Young (Allocation Failure)
[0.099s][info][gc,heap ] GC(0) DefNew: 11416K->1471K(13696K)
[0.099s][info][gc,heap ] GC(0) Tenured: 0K->294K(5312K)
[0.099s][info][gc,metaspace ] GC(0) Metaspace: 6103K->6103K(1056768K)
[0.099s][info][gc ] GC(0) Pause Young (Allocation Failure) 11M->1M(18M) 2.322ms
[0.099s][info][gc,cpu ] GC(0) User=0.00s Sys=0.00s Real=0.01s
end
[0.099s][info][gc,heap,exit ] Heap
[0.099s][info][gc,heap,exit ] def new generation total 13696K, used 2934K [0x00000000fec00000, 0x00000000ffad0000, 0x00000000ffad0000)
[0.099s][info][gc,heap,exit ] eden space 12224K, 11% used [0x00000000fec00000, 0x00000000fed6d908, 0x00000000ff7f0000)
[0.099s][info][gc,heap,exit ] from space 1472K, 99% used [0x00000000ff960000, 0x00000000ffacfff8, 0x00000000ffad0000)
[0.099s][info][gc,heap,exit ] to space 1472K, 0% used [0x00000000ff7f0000, 0x00000000ff7f0000, 0x00000000ff960000)
[0.099s][info][gc,heap,exit ] tenured generation total 5312K, used 294K [0x00000000ffad0000, 0x0000000100000000, 0x0000000100000000)
[0.099s][info][gc,heap,exit ] the space 5312K, 5% used [0x00000000ffad0000, 0x00000000ffb19960, 0x00000000ffb19a00, 0x0000000100000000)
[0.099s][info][gc,heap,exit ] Metaspace used 6164K, capacity 6251K, committed 6528K, reserved 1056768K
[0.099s][info][gc,heap,exit ] class space used 532K, capacity 570K, committed 640K, reserved 1048576K
可以看到新生代使用
15M
空間,
eden區
占了
12288K
,完全滿足了
10MB
需要,并沒有發生
GC
(日志的
GC
隻是在
for
循環結束後産生的,一次性回收了
10M
)。
實際工作中,應根據系統的特點,做合理的設定,基本政策是:
- 盡可能将對象預留在新生代
- 減少老年代
次數GC
另外,可以使用
-XX:NewRatio=老年代/新生代
指定新生代和老年代的比例,比如使用參數:
-Xmx20m
-Xms20m
-XX:NewRatio=2
-Xlog:gc*
-XX:+UseSerialGC
[0.005s][info][gc] Using Serial
[0.005s][info][gc,heap,coops] Heap address: 0x00000000fec00000, size: 20 MB, Compressed Oops mode: 32-bit
[0.096s][info][gc,start ] GC(0) Pause Young (Allocation Failure)
[0.097s][info][gc,heap ] GC(0) DefNew: 4852K->639K(6144K)
[0.097s][info][gc,heap ] GC(0) Tenured: 0K->1112K(13696K)
[0.097s][info][gc,metaspace ] GC(0) Metaspace: 5905K->5905K(1056768K)
[0.097s][info][gc ] GC(0) Pause Young (Allocation Failure) 4M->1M(19M) 1.413ms
[0.097s][info][gc,cpu ] GC(0) User=0.00s Sys=0.00s Real=0.00s
[0.098s][info][gc,start ] GC(1) Pause Young (Allocation Failure)
[0.099s][info][gc,heap ] GC(1) DefNew: 5920K->0K(6144K)
[0.099s][info][gc,heap ] GC(1) Tenured: 1112K->2776K(13696K)
[0.099s][info][gc,metaspace ] GC(1) Metaspace: 5970K->5970K(1056768K)
[0.099s][info][gc ] GC(1) Pause Young (Allocation Failure) 6M->2M(19M) 1.129ms
[0.099s][info][gc,cpu ] GC(1) User=0.00s Sys=0.01s Real=0.00s
[0.100s][info][gc,heap,exit ] Heap
[0.100s][info][gc,heap,exit ] def new generation total 6144K, used 2238K [0x00000000fec00000, 0x00000000ff2a0000, 0x00000000ff2a0000)
[0.100s][info][gc,heap,exit ] eden space 5504K, 40% used [0x00000000fec00000, 0x00000000fee2f690, 0x00000000ff160000)
[0.100s][info][gc,heap,exit ] from space 640K, 0% used [0x00000000ff160000, 0x00000000ff160398, 0x00000000ff200000)
[0.100s][info][gc,heap,exit ] to space 640K, 0% used [0x00000000ff200000, 0x00000000ff200000, 0x00000000ff2a0000)
[0.100s][info][gc,heap,exit ] tenured generation total 13696K, used 2776K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000)
[0.100s][info][gc,heap,exit ] the space 13696K, 20% used [0x00000000ff2a0000, 0x00000000ff556250, 0x00000000ff556400, 0x0000000100000000)
[0.100s][info][gc,heap,exit ] Metaspace used 5998K, capacity 6091K, committed 6272K, reserved 1056768K
[0.100s][info][gc,heap,exit ] class space used 517K, capacity 538K, committed 640K, reserved 1048576K
堆大小為
20M
,新生代和老年代的比為
1:2
,是以新生代大小約為
7M
,老年代為
13M
,配置設定
1M
時,由于
from/to
空間不足,導緻兩個
1MB
的數組進入了老年代。
4.3 堆溢出處理
如果在
Java
程式運作過程中,堆空間不足,會抛出記憶體溢出錯誤,也就是常見的
OOM
。想要分析原因,可以使用參數
-XX:+HeapDumpOnOutOfMemoryError
,可以在記憶體溢出時導出整個堆的資訊,配合使用的還有
-XX:HeapDumpPath
,指定導出堆的存放路徑,例子如下:
public static void main(String[] args){
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 25; i++) {
list.add(new byte[1*1024*1024]);
}
}
-Xmx20m
-Xms5m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=out.dump
例子配置設定了
25M
的記憶體,但是堆隻有
20M
,會抛出
OOM
,并且檔案儲存到
out.dump
中,注意該檔案是二進制檔案,需要使用專業的工具(如
MAT
等)檢視。
5 非堆參數
5.1 方法區
從
JDK8
開始,永久區被移除,使用了新的中繼資料區來存放類的中繼資料,預設情況下,中繼資料區受系統可用記憶體的限制,但是仍然可以使用
-XX:MaxMetaspaceSize
指定永久區的最大可用值。
5.2 棧
棧是每個線程私有的空間,可以使用
-Xss
指定線程的棧大小,具體在筆者之前的文章中。
5.3 直接記憶體
直接記憶體跳過了
Java堆
,可以使得程式直接通路原生堆空間,在一定程度上加快了記憶體空間的通路速度。最大可用直接記憶體可以使用
-XX:MaxDirectMemorySize
設定,如果不設定,預設為最大堆空間,即
-Xmx
的值,當直接記憶體使用量到達最大值時,會觸發
GC
,如果
GC
後不能有效釋放足夠的空間,直接記憶體依然會引起系統的
OOM
下面測試一下直接記憶體與堆的速度:
public class Main {
public static final int count = 1000000;
public static void directAccess(){
long start = System.currentTimeMillis();
ByteBuffer b = ByteBuffer.allocateDirect(500);
for(int i=0;i<count;++i){
for (int j = 0; j < 99; j++) {
b.putInt(j) ;
}
b.flip();
for (int j = 0; j < 99; j++) {
b.getInt();
}
b.clear();
}
long end = System.currentTimeMillis();
System.out.println("Direct access: "+(end-start)+" ms");
}
public static void bufferAccess(){
long start = System.currentTimeMillis();
ByteBuffer b = ByteBuffer.allocate(500);
for(int i=0;i<count;++i){
for (int j = 0; j < 99; j++) {
b.putInt(j) ;
}
b.flip();
for (int j = 0; j < 99; j++) {
b.getInt();
}
b.clear();
}
long end = System.currentTimeMillis();
System.out.println("Buffer access: "+(end-start)+" ms");
}
public static void main(String[] args){
directAccess();
bufferAccess();
directAccess();
bufferAccess();
}
}
輸出(不帶任何參數):
Direct access: 167 ms
Buffer access: 70 ms
Direct access: 176 ms
Buffer access: 67 ms
直接記憶體的通路速度要快于堆記憶體,但是有一個缺點就是申請的時候速度慢:
public static void directAllocate(){
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1000);
}
long end = System.currentTimeMillis();
System.out.println("Direct allocate: "+(end-start)+" ms");
}
public static void bufferAllocate(){
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1000);
}
long end = System.currentTimeMillis();
System.out.println("Buffer allocate: "+(end-start)+" ms");
}
public static void main(String[] args){
directAllocate();
bufferAllocate();
directAllocate();
bufferAllocate();
}
Direct allocate: 867 ms
Buffer allocate: 287 ms
Direct allocate: 676 ms
Buffer allocate: 208 ms
簡單來說,直接記憶體适合申請次數較少、通路較頻繁的場合,如果需要頻繁申請記憶體空間,并不适合使用直接記憶體。