JVM:第二章:設計一個剛好在一秒堆溢出的程序
創(chuàng)建了一個JVMDemo類:
package common;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.ArrayList;
import java.util.List;
/**
* @Author:Liaozhiwei
* @Date: 2019/11/23 18:45
*/
public class JVMDemo {
/**
* JVM內(nèi)存分配測試
* 堆內(nèi)存30M|Yong10M(Eden8M,from1M,to1M)|Old20M
*初始堆大小:-Xms30m 最大堆大小:-Xmx30m
* 讓虛擬機在發(fā)生內(nèi)存溢出時 Dump 出當前的內(nèi)存堆轉儲快照,以便分析用:-XX:+HeapDumpOnOutOfMemoryError
* 打印 GC 信息:-XX:+PrintGCDetails
* 永久代(方法區(qū))的最大值:-XX:MaxPermSize=20M
*/
byte[] a = new byte[1024*1024*1];//1M
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int i =1;
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
System.out.println("初始化堆的當前內(nèi)存使用量:"+memoryMXBean.getHeapMemoryUsage());
System.out.println("初始化非堆內(nèi)存的當前內(nèi)存使用量:"+memoryMXBean.getNonHeapMemoryUsage());
while (true){
list.add(new JVMDemo());
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("非堆內(nèi)存的當前內(nèi)存使用量:"+memoryMXBean.getNonHeapMemoryUsage());
System.out.println("堆內(nèi)存的當前內(nèi)存使用量:"+memoryMXBean.getHeapMemoryUsage());
System.out.println(i++);
}
}
}
配置JVM:
控制臺輸出:
初始化堆的當前內(nèi)存使用量:init = 31457280(30720K) used = 2083952(2035K) committed = 30408704(29696K) max = 30408704(29696K)
可以看到我給設置了30M堆內(nèi)存,初始化的時候已經(jīng)被使用了2035k了,最大可使用的堆內(nèi)存為29M,因為新生代from區(qū)與to區(qū)二個只能二選一,必須有一個為空的,所以最大可使用29M。
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 3302664(3225K) committed = 30408704(29696K) max = 30408704(29696K)
1
進入循環(huán),可以看到增加了1190k
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 4351256(4249K) committed = 30408704(29696K) max = 30408704(29696K)
2
這次增加了1024k,也就是1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 5399848(5273K) committed = 30408704(29696K) max = 30408704(29696K)
3
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 6616232(6461K) committed = 30408704(29696K) max = 30408704(29696K)
4
增加了1188k
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 7832592(7649K) committed = 30408704(29696K) max = 30408704(29696K)
5
增加了1188k,現(xiàn)在已經(jīng)7649k了,離8m(8192k)只有543k的內(nèi)存空間了
[GC (Allocation Failure) [PSYoungGen: 7649K->984K(9216K)] 7649K->6112K(29696K), 0.0019481 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
然后開始新生代gc,新生代已使用7649k,gc之后新生代已使用984k,新生代總大小9M,GC前Java堆已使用容量7649k,GC后Java堆已使用容量6112k,總堆的大小29M。
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 7454896(7280K) committed = 30408704(29696K) max = 30408704(29696K)
6
從6112k,增加了1168k,到7280k
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 8503488(8304K) committed = 30408704(29696K) max = 30408704(29696K)
7
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 9552080(9328K) committed = 30408704(29696K) max = 30408704(29696K)
8
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 10600672(10352K) committed = 30408704(29696K) max = 30408704(29696K)
9
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 11649264(11376K) committed = 30408704(29696K) max = 30408704(29696K)
10
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 12697856(12400K) committed = 30408704(29696K) max = 30408704(29696K)
11
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 13746448(13424K) committed = 30408704(29696K) max = 30408704(29696K)
12
增加了1M
[GC (Allocation Failure) [PSYoungGen:
8296K->1000K(9216K)] 13424K->13296K(29696K), 0.0016737 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
開始新生代gc,新生代已使用8296k,gc之后新生代已使用1000k,新生代總大小9M,GC前Java堆已使用容量13424k,GC后Java堆已使用容量13296k,總堆的大小29M。
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 14902416(14553K) committed = 30408704(29696K) max = 30408704(29696K)
13
從13296k增加了1257k到14553k
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 15951008(15577K) committed = 30408704(29696K) max = 30408704(29696K)
14
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 16999600(16601K) committed = 30408704(29696K) max = 30408704(29696K)
15
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 18048192(17625K) committed = 30408704(29696K) max = 30408704(29696K)
16
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 19096784(18649K) committed = 30408704(29696K) max = 30408704(29696K)
17
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 20145376(19673K) committed = 30408704(29696K) max = 30408704(29696K)
18
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 21193968(20697K) committed = 30408704(29696K) max = 30408704(29696K)
19
增加了1M
[GC (Allocation Failure) [PSYoungGen:
8401K->1016K(9216K)] 20697K->20488K(29696K), 0.0015551 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
開始新生代gc,新生代已使用8401k,gc之后新生代已使用1016k,新生代總大小9M,GC前Java堆已使用容量20697k,GC后Java堆已使用容量20488k,總堆的大小29M,剩余9208k,小于新生代9216k,觸發(fā)FullGC
[Full GC (Ergonomics) [PSYoungGen: 1016K->0K(9216K)]
[ParOldGen: 19472K->20243K(20480K)] 20488K->20243K(29696K),
[Metaspace: 3416K->3416K(1056768K)], 0.0097494 secs] [Times:
user=0.00 sys=0.00, real=0.00 secs]
新生代gc,新生代已使用1016k,gc之后新生代已使用0k,新生代總大小9M
老年代gc,老年代已使用19472k,gc之后老年代已使用20243k,老年代總大小20M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 21935136(21421K) committed = 30408704(29696K) max = 30408704(29696K)
20
增加了1178k
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 22983728(22445K) committed = 30408704(29696K) max = 30408704(29696K)
21
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 24032320(23469K) committed = 30408704(29696K) max = 30408704(29696K)
22
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 25080912(24493K) committed = 30408704(29696K) max = 30408704(29696K)
23
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 26129504(25517K) committed = 30408704(29696K) max = 30408704(29696K)
24
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 27178096(26541K) committed = 30408704(29696K) max = 30408704(29696K)
25
增加了1M
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 28226688(27565K) committed = 30408704(29696K) max = 30408704(29696K)
26
增加了1M
[Full GC (Ergonomics) [PSYoungGen: 7321K->7168K(9216K)]
[ParOldGen: 20243K->20243K(20480K)] 27565K->27411K(29696K),
[Metaspace: 3419K->3419K(1056768K)], 0.0087983 secs] [Times:
user=0.00 sys=0.00, real=0.00 secs]
新生代gc,新生代已使用7321K,gc之后新生代已使用7168K,新生代總大小9M
老年代gc,老年代已使用20243K,gc之后老年代已使用20243K,老年代總大小20M
[Full GC
(Allocation Failure) [PSYoungGen: 7168K->7168K(9216K)] [ParOldGen:
20243K->20225K(20480K)] 27411K->27394K(29696K), [Metaspace:
3419K->3419K(1056768K)], 0.0091156 secs] [Times: user=0.11 sys=0.00,
real=0.02 secs]
新生代gc,新生代已使用7168K,gc之后新生代已使用7168K,新生代總大小9M
老年代gc,老年代已使用20243K,gc之后老年代已使用20225K,老年代總大小20M
三種時間類型:
real: 程序從開始到結束所用的時鐘時間。這個時間包括其他進程使用的時間片和進程阻塞的時間(比如等待 I/O 完成)。
user:進程執(zhí)行用戶態(tài)代碼(核心之外)所使用的時間。這是執(zhí)行此進程所使用的實際 CPU 時間,其他進程和此進程阻塞的時間并不包括在內(nèi)。在垃圾收集的情況下,表示 GC 線程執(zhí)行所使用的 CPU 總時間。
sys:進程在內(nèi)核態(tài)消耗的 CPU 時間,即在內(nèi)核執(zhí)行系統(tǒng)調(diào)用或等待系統(tǒng)事件所使用的 CPU 時間。
user + sys 時間告訴我們程序執(zhí)行實際使用的 CPU 時間。注意這里指所有的
CPU,因此如果在進程里有多個線程的話,這個時間可能會超過 real 所表示的時鐘時間。如果出現(xiàn)user + sys 時間的和比 real
時間要大,這主要是因為日志時間是從 JVM 中獲得的,而這個 JVM 在多核的處理器上被配置了多個 GC 線程,由于多個線程并行地執(zhí)行
GC,因此整個 GC 工作被這些線程共享,最終導致實際的時鐘時間(real)小于總的 CPU 時間(user + sys)。
在做性能優(yōu)化時,我們一般采用 real 時間來優(yōu)化程序。因為最終用戶只關心點擊頁面發(fā)出請求到頁面上展示出內(nèi)容所花的時間,也就是響應時間,而不關心你到底使用了多少個 GC 線程或者處理器。但并不是說 sys 和 user 兩個時間不重要,當我們想通過增加 GC 線程或者 CPU 數(shù)量來減少 GC 停頓時間時,可以參考這兩個時間。
所以可以根據(jù)使用(1秒 - real占用的時間)= 休眠的時間,這樣休眠的時間加上GC使用的時間(real時間)就可以就可以設計出剛好一秒堆溢出
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid13144.hprof ...
Heap dump file created [28907711 bytes in 0.034 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at common.StringTool.<init>(StringTool.java:206)
at common.JVMDemo.main(JVMDemo.java:30)
Heap
PSYoungGen total 9216K, used 7406K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 90% used [0x00000000ff600000,0x00000000ffd3bb20,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 20480K, used 20225K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
object space 20480K, 98% used [0x00000000fe200000,0x00000000ff5c07e8,0x00000000ff600000)
Metaspace used 3451K, capacity 4564K, committed 4864K, reserved 1056768K
class space used 370K, capacity 388K, committed 512K, reserved 1048576K
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=20M; support was removed in 8.0
Process finished with exit code 1
問題:為啥每次加的都不是1M呢?
解釋:每次加的都不是1M,原因一:ArrayList擴容影響的,通過ArrrayList空參構造器創(chuàng)建對象。底層創(chuàng)建一個長度為10的數(shù)組,當我們向數(shù)組中添加11個元素時,底層會進行擴容,擴容為原來的1.5倍(創(chuàng)建一個新的數(shù)組,長度為原數(shù)組長度的1.5倍,將原數(shù)組復制到新數(shù)組中)。改造ArrayList,驗證想法:
運行之后,控制臺打印:
初始化堆的當前內(nèi)存使用量:init = 31457280(30720K) used = 3089664(3017K) committed = 30408704(29696K) max = 30408704(29696K)
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 4138256(4041K) committed = 30408704(29696K) max = 30408704(29696K)
1
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 5186848(5065K) committed = 30408704(29696K) max = 30408704(29696K)
2
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 6235440(6089K) committed = 30408704(29696K) max = 30408704(29696K)
3
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 7284032(7113K) committed = 30408704(29696K) max = 30408704(29696K)
4
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 8332624(8137K) committed = 30408704(29696K) max = 30408704(29696K)
5
[GC (Allocation Failure) [PSYoungGen: 8137K->1023K(9216K)]
8137K->6252K(29696K), 0.0069038 secs] [Times: user=0.00 sys=0.00,
real=0.01 secs]
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 7613624(7435K) committed = 30408704(29696K) max = 30408704(29696K)
6
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 8913912(8704K) committed = 30408704(29696K) max = 30408704(29696K)
7
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 9962504(9729K) committed = 30408704(29696K) max = 30408704(29696K)
8
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 11011096(10753K) committed = 30408704(29696K) max = 30408704(29696K)
9
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 12059688(11777K) committed = 30408704(29696K) max = 30408704(29696K)
10
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 13108280(12801K) committed = 30408704(29696K) max = 30408704(29696K)
11
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 14156872(13825K) committed = 30408704(29696K) max = 30408704(29696K)
12
[GC (Allocation Failure) [PSYoungGen: 8596K->920K(9216K)]
13825K->13324K(29696K), 0.0025148 secs] [Times: user=0.00 sys=0.00,
real=0.00 secs]
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 14855728(14507K) committed = 30408704(29696K) max = 30408704(29696K)
13
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 15904320(15531K) committed = 30408704(29696K) max = 30408704(29696K)
14
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 16952912(16555K) committed = 30408704(29696K) max = 30408704(29696K)
15
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 18085408(17661K) committed = 30408704(29696K) max = 30408704(29696K)
16
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 19134000(18685K) committed = 30408704(29696K) max = 30408704(29696K)
17
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 20182592(19709K) committed = 30408704(29696K) max = 30408704(29696K)
18
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 21231184(20733K) committed = 30408704(29696K) max = 30408704(29696K)
19
[GC (Allocation Failure) [PSYoungGen: 8328K->1000K(9216K)]
20733K->20580K(29696K), 0.0023324 secs] [Times: user=0.06 sys=0.03,
real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen:
1000K->0K(9216K)] [ParOldGen: 19580K->20310K(20480K)]
20580K->20310K(29696K), [Metaspace: 3180K->3180K(1056768K)],
0.0108207 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 22009368(21493K) committed = 30408704(29696K) max = 30408704(29696K)
20
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 23057960(22517K) committed = 30408704(29696K) max = 30408704(29696K)
21
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 24106552(23541K) committed = 30408704(29696K) max = 30408704(29696K)
22
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 25155144(24565K) committed = 30408704(29696K) max = 30408704(29696K)
23
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 26203736(25589K) committed = 30408704(29696K) max = 30408704(29696K)
24
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 27252328(26613K) committed = 30408704(29696K) max = 30408704(29696K)
25
堆內(nèi)存的當前內(nèi)存使用量:init = 31457280(30720K) used = 28300920(27637K) committed = 30408704(29696K) max = 30408704(29696K)
26
[Full GC (Ergonomics) [PSYoungGen: 7326K->7168K(9216K)]
[ParOldGen: 20310K->20310K(20480K)] 27637K->27478K(29696K),
[Metaspace: 3182K->3182K(1056768K)], 0.0042343 secs] [Times:
user=0.02 sys=0.00, real=0.01 secs]
[Full GC (Allocation
Failure) [PSYoungGen: 7168K->7168K(9216K)] [ParOldGen:
20310K->20296K(20480K)] 27478K->27464K(29696K), [Metaspace:
3182K->3182K(1056768K)], 0.0078410 secs] [Times: user=0.00 sys=0.00,
real=0.01 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to C:\Users\Administrator\Desktop\error_logs ...
Unable to create C:\Users\Administrator\Desktop\error_logs: No such file or directory
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at utils.OneM.<init>(OneM.java:9)
at utils.JVMDemo.main(JVMDemo.java:37)
Heap
PSYoungGen total 9216K, used 7409K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 90% used [0x00000000ff600000,0x00000000ffd3c738,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 20480K, used 20296K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
object space 20480K, 99% used [0x00000000fe200000,0x00000000ff5d20b0,0x00000000ff600000)
Metaspace used 3214K, capacity 4556K, committed 4864K, reserved 1056768K
class space used 346K, capacity 392K, committed 512K, reserved 1048576K
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
Disconnected from the target VM, address: '127.0.0.1:65390', transport: 'socket'
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=20M; support was removed in 8.0
Process finished with exit code 1
可以看到基本都是1M了
問題2:為什么同一程序所花費的時間都不同?
解釋:同樣的程序每次運行的時間不一樣,原因是:
1、與操作系統(tǒng)的調(diào)度有關
2、現(xiàn)在的CPU支持動態(tài)調(diào)頻
所以每次執(zhí)行程序運行的時間都不同
以上若有任何疑問或者錯誤歡迎在評論下面指出,我會及時更改