JAVA的垃圾收集器与内存分配策略【一篇文章直接看懂】( 七 )


在x86系统中,进程共用内存,不隔离 。使用分页管理机制,实现线性地址到物理地址空间的映射 。故而,linux/x86-64的ZGC使用了多重映射(Multi-)实现多个虚拟地址映射到同一个内存地址上【n-1】===》ZGC在虚拟地址识别的空间大于物理上的 。
染色指针中的标志位看作分段符,将这些不同的地址段映射到同一个内存空间,就可以正常寻址了 。【原本是一个整体,现在切开了】
运行过程(四大阶段皆可并发)
并发标记( Mark):遍历对象图做可达性分析,但是在指针上标记,更新染色指针的 0、 1并发预备分配(for ):根据查询得出清理哪些 。每次GC扫描所有,扫描成本换记忆集维护成本 。故重分配集只是决定存活的对象复制到别的中 。并发重分配( ):把重分配集存活的对象复制到新的中,并为每个维护一个转发表( Table),记录旧->新的引用 。并且可以只从染色指针的引用上明确得知一个对象是否处于重分配集中,若用户线程访问当前对象,可以被预置的内存屏障所拦截,根据转发表转发到新的对象上,并修正引用值,称为指针的“自愈”(Self-)能力 。好处:1. 只有第一次会转发,比之前的每次的开销低 。2. 中的存活对象都复制后可以立即用于新对象的分配【转发表要留着】并发重映射( Remap):修正整个堆中指向重分配集中就对象的所有引用 。但不是迫切任务 。因为引用是可以自愈的,故而合并到了下一次GC的并发标记中完成【因为都要遍历所有对象】
ZGC是迄今为止最前沿的成果,几乎所有收集过程可并发,短暂停留只与GC Roots大小相关,在任何堆上都小于10ms
但是:
因为没有分代,所以能承受的对象分配率不会太高 。【对一个大堆并发收集时,因为新对象的分配率高,所以有大量的新对象,ZGC只能全都当作存活对象,但是其中大多数是很快就死的===》产生了大量的浮动垃圾】,解决这个问题只能引入分代收集 。
性能方面:处于实验阶段
下图:左:吞吐量测试,右:ZGC停顿时间测试
PS:他也支持"NUMA-Aware"内存分配【专为多CPU/多核处理器】
选择合适的垃圾收集器 收集器
一款不能够进行垃圾收集为卖点的垃圾收集器 。但是还是有”自动内存管理子系统“的功能,这是GC收集器除了GC之外的工作 。
如果应用只要运行数分钟甚至数秒,只要Java虚拟机能正确分配内存,在堆耗尽之前就会退出,那显然运行负载极小、没有任何回收行为的便是很恰当的选择 。
收集器的权衡
应用程序:
B/S系统==》延迟时间
钱多==》商用的Vega、Zing VM
钱不够要延迟低能用新版本==》ZGC
要稳定并在系统==》
遗留系统==》CMS,大内存G1
虚拟机及垃圾收集器日记
-Xlog参数:
-Xlog[:[][:[][:[][:-]]]]
其中最关键的是由tag【某个功能块的名字,如gc】与level【日记级别】共同组成 。
日志级别:Trace,Debug,Info,,Error,Off,决定了详细程度
的日志规则与Log4j、SLF4j框架一致 。如果不置顶,默认值是\level\tags:
[3.080s][info][gc,cpu] GC(5) User=0.03s Sys=0.00s Real=0.01s
【JAVA的垃圾收集器与内存分配策略【一篇文章直接看懂】】内存分配与回收策略