JVM:第二章:设计一个刚好在一秒堆溢出的程序

MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();

System.out.println(“初始化堆的当前内存使用量:”+memoryMXBean.getHeapMemoryUsage());

System.out.println(“初始化非堆内存的当前内存使用量:”+memoryMXBean.getNonHeapMemoryUsage());

while (true){

list.add(new JVMDemo());

try {

Thread.sleep(40);

} catch (InterruptedException e) {

e.printStackTrace();

}

// System.out.println(“非堆内存的当前内存使用量:”+memoryMXBean.getNonHeapMemoryUsage());

System.out.println(“堆内存的当前内存使用量:”+memoryMXBean.getHeapMemoryUsage());

System.out.println(i++);

}

}

}

配置JVM:

-XX:MaxPermSize=20M -Xms30m -Xmx30m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:HeapDumpPath=C:\Users\Administrator\Desktop\error_logs

控制台输出:

初始化堆的当前内存使用量:init = 31457280(30720K) used = 2083952(2035K) committed = 30408704(29696K) max = 30408704(29696K)

可以看到我给设置了30M堆内存,初始化的时候已经被使用了2035k了,最大可使用的堆内存为29M,因为新生代from区与to区二个只能二选一,必须有一个为空的,所以最大可使用29M。

**堆内存的当前内存使用量:init = 31457280(30720K) used = 3302664(3225K) committed = 30408704(29696K) max = 30408704(29696K)

1**

进入循环,可以看到增加了1190k

**堆内存的当前内存使用量:init = 31457280(30720K) used = 4351256(4249K) committed = 30408704(29696K) max = 30408704(29696K)

2**

这次增加了1024k,也就是1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 5399848(5273K) committed = 30408704(29696K) max = 30408704(29696K)

3**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 6616232(6461K) committed = 30408704(29696K) max = 30408704(29696K)

4**

增加了1188k

**堆内存的当前内存使用量:init = 31457280(30720K) used = 7832592(7649K) committed = 30408704(29696K) max = 30408704(29696K)

5**

增加了1188k,现在已经7649k了,离8m(8192k)只有543k的内存空间了

[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。

**堆内存的当前内存使用量:init = 31457280(30720K) used = 7454896(7280K) committed = 30408704(29696K) max = 30408704(29696K)

6**

从6112k,增加了1168k,到7280k

**堆内存的当前内存使用量:init = 31457280(30720K) used = 8503488(8304K) committed = 30408704(29696K) max = 30408704(29696K)

7**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 9552080(9328K) committed = 30408704(29696K) max = 30408704(29696K)

8**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 10600672(10352K) committed = 30408704(29696K) max = 30408704(29696K)

9**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 11649264(11376K) committed = 30408704(29696K) max = 30408704(29696K)

10**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 12697856(12400K) committed = 30408704(29696K) max = 30408704(29696K)

11**

增加了1M

**堆内存的当前内存使用量: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。

**堆内存的当前内存使用量:init = 31457280(30720K) used = 14902416(14553K) committed = 30408704(29696K) max = 30408704(29696K)

13**

从13296k增加了1257k到14553k

**堆内存的当前内存使用量:init = 31457280(30720K) used = 15951008(15577K) committed = 30408704(29696K) max = 30408704(29696K)

14**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 16999600(16601K) committed = 30408704(29696K) max = 30408704(29696K)

15**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 18048192(17625K) committed = 30408704(29696K) max = 30408704(29696K)

16**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 19096784(18649K) committed = 30408704(29696K) max = 30408704(29696K)

17**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 20145376(19673K) committed = 30408704(29696K) max = 30408704(29696K)

18**

增加了1M

**堆内存的当前内存使用量: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,触发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

**堆内存的当前内存使用量:init = 31457280(30720K) used = 21935136(21421K) committed = 30408704(29696K) max = 30408704(29696K)

20**

增加了1178**k

堆内存的当前内存使用量:init = 31457280(30720K) used = 22983728(22445K) committed = 30408704(29696K) max = 30408704(29696K)

21**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 24032320(23469K) committed = 30408704(29696K) max = 30408704(29696K)

22**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 25080912(24493K) committed = 30408704(29696K) max = 30408704(29696K)

23**

增加了1M

**堆内存的当前内存使用量:init = 31457280(30720K) used = 26129504(25517K) committed = 30408704(29696K) max = 30408704(29696K)

24**

**堆内存的当前内存使用量:init = 31457280(30720K) used = 27178096(26541K) committed = 30408704(29696K) max = 30408704(29696K)

25**

增加了1M

**堆内存的当前内存使用量: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:进程执行用户态代码(核心之外)所使用的时间。这是执行此进程所使用的实际 CPU 时间,其他进程和此进程阻塞的时间并不包括在内。在垃圾收集的情况下,表示 GC 线程执行所使用的 CPU 总时间。

  • sys:进程在内核态消耗的 CPU 时间,即在内核执行系统调用或等待系统事件所使用的 CPU 时间。

  • user + sys 时间告诉我们程序执行实际使用的 CPU 时间。注意这里指所有的 CPU,因此如果在进程里有多个线程的话,这个时间可能会超过 real 所表示的时钟时间。如果出现user + sys 时间的和比 real 时间要大,这主要是因为日志时间是从 JVM 中获得的,而这个 JVM 在多核的处理器上被配置了多个 GC 线程,由于多个线程并行地执行 GC,因此整个 GC 工作被这些线程共享,最终导致实际的时钟时间(real)小于总的 CPU 时间(user + sys)。

在做性能优化时,我们一般采用 real 时间来优化程序。因为最终用户只关心点击页面发出请求到页面上展示出内容所花的时间,也就是响应时间,而不关心你到底使用了多少个 GC 线程或者处理器。但并不是说 sys 和 user 两个时间不重要,当我们想通过增加 GC 线程或者 CPU 数量来减少 GC 停顿时间时,可以参考这两个时间。

所以可以根据使用(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.(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™ 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空参构造器创建对象。底层创建一个长度为10的数组,当我们向数组中添加11个元素时,底层会进行扩容,扩容为原来的1.5倍(创建一个新的数组,长度为原数组长度的1.5倍,将原数组复制到新数组中)。改造ArrayList,验证想法:

运行之后,控制台打印:

初始化堆的当前内存使用量:init = 31457280(30720K) used = 3089664(3017K) committed = 30408704(29696K) max = 30408704(29696K)

堆内存的当前内存使用量:init = 31457280(30720K) used = 4138256(4041K) committed = 30408704(29696K) max = 30408704(29696K)

1

堆内存的当前内存使用量:init = 31457280(30720K) used = 5186848(5065K) committed = 30408704(29696K) max = 30408704(29696K)

2

堆内存的当前内存使用量:init = 31457280(30720K) used = 6235440(6089K) committed = 30408704(29696K) max = 30408704(29696K)

3

堆内存的当前内存使用量:init = 31457280(30720K) used = 7284032(7113K) committed = 30408704(29696K) max = 30408704(29696K)

4

堆内存的当前内存使用量:init = 31457280(30720K) used = 8332624(8137K) committed = 30408704(29696K) max = 30408704(29696K)

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-po3LWaPd-1715790290452)]

[外链图片转存中…(img-55GoVLaK-1715790290453)]

[外链图片转存中…(img-B0cGmsO7-1715790290453)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值