美文网首页
你还不知道的影响Java GC的其他考虑因素的知识点

你还不知道的影响Java GC的其他考虑因素的知识点

作者: java涛仔 | 来源:发表于2021-12-03 16:41 被阅读0次

    你可能还不知道的知识点

    1. GC只适合管理内存不适合管理外部资源;

    2) finalize和weak/soft/phantom reference等是借助GC起作用的;

    3)RMI (Remote Method Invocation)中有一项分布式垃圾回收技术DGC (Distributed Garbage Collection),解决远程引用对象的垃圾回收问题;

    4)JDK 8已经取消永久代,class metadata也从永久代转移到jvm native内存(如果你们公司的JVM监控依然显示永久代,那应该指的class metadata占用的大小);

    5)类的卸载也是由GC完成的

    影响GC的因素

    影响GC的因素主要有堆大小、垃圾回收器及代码使用内存的方式等,除此以外,还有其他因素影响GC,分析GC问题是也要考虑到,即所谓的other considerations。

    影响GC的其他因素

    1) finalization

    2) weak/soft/phantom reference

    3)显式FullGC调用System.gc()

    4)-XX:+DisableExplicitGC 选项

    5) server/client模式

    6) 

    -XX:SoftRefLRUPolicyMSPerMB选项

    7) 类的卸载、MetaspaceSize选项、MaxMetaspaceFreeRatio选项、MinMetaspaceFreeRatio选项、MaxMetaspaceFreeRatio选项

    1) Finalization and Weak, Soft, and Phantom References

    在java中,finalization和weak/soft/phantom reference是java的特色内存管理工具,也可以说是垃圾回收器的访问接口,因为app可以通过使用这些工具间接地与GC交互,所以它们会影响GC。官方文档说,这些工具可能导致java程序性能缺陷 (performance artifacts)。

    那么,app使用finalization或weak/soft/phantom reference而导致性能变差,这跟GC有什么关系呢?这一点官方文档未讲明白,但是它举了一个例子。

    使用finalization的一个典型例子就是:利用finalization来关闭文件描述符,这样就让外部资源依赖上了GC。

    看到这里,更加困惑了,让外部资源依赖GC又跟性能变坏有什么关联?

    写到这里,官方文档还是没有讲清楚,只是轻描淡写的说了一句"Relying on garbage collection to manage resources other than memory is almost always a bad idea"。仔细读这句话好几遍,才弄明白它想表达的意思:GC是用来管理内存的,不是管理外部资源的,所以像上面这种用GC关闭文件的做法是bad idea。

    "使用finalization或weak/soft/phantom reference而导致性能变差,这跟GC有什么关系呢"似乎有了答案,即使用finalization或weak/soft/phantom reference来管理外部资源,间接让GC管理外部资源,导致java程序性能下降。

    虽然官方文档说"These features can create performance artifacts at the Java programming language level",似乎是让我们尽量不要用finalization和weak/soft/phantom reference,使用它们是bad idea,但实际上,性能变差的真正原因在于不正确的使用finalization和weak/soft/phantom reference (用它们管理外部资源),只要是用finalization和weak/soft/phantom reference来管理内存而不是外部资源就没问题

    喜欢刨根问底的朋友可以进一步研究下:为什么用GC间接管理外部资源会导致性能下降

    话又说回来,finalization还有其他性能问题:1)JVM在new一个使用了finalize()的对象时,要把它记录到一个队列中去,这就多一次内存访问;2)使用了finalize()的对象在变成垃圾时,那些被它引用的对象还不能立即被标成垃圾,这拖慢了GC效率;3)JVM的finalizer线程需要把可回收的对象从finalize队列取出,调用它的finalize()方法,然后把它标记为finalized,这些操作都会消耗资源,影响性能。所以java官方极不推荐使用finalization。

    附上讨论finalization缺点及避免方法的文章:

    https://www.devx.com/Java/Article/30192

    2) Explicit Garbage Collection

    调用System.gc()会触发Full GC,所以当然也会影响GC,并且有的程序的确会在代码中显式调用System.gc()。在不恰当的时机调用System.gc()可能导致性能不好。如果项目代码中有调用System.gc(),可以看看是不是这个调用导致性能差的。可以通过-XX:+DisableExplicitGC 选项让JVM忽略System.gc()调用。

    针对显式GC这项功能,官方文档又很模糊的说它"in general should be avoided",即叫我们不要使用。然而,啪啪打脸的是,它又举例说明Explicit Garbage Collection应用在了RMI的DGC技术中,用于解决跨JVM的对象回收问题。

    现在,我们只要知道,System.gc()和-XX:+DisableExplicitGC选项影响GC,我们要尽量少用System.gc()即可。

    3) Soft References

    前面已提到soft reference会影响GC,这里补充上与soft reference相关的,同样影响GC的因素,即server模式和

    -XX:SoftRefLRUPolicyMSPerMB=选项。

    我们知道JVM有server模式和client模式,这也会影响GC。在server模式中,soft reference存活的时间一般比client模式长,但是可以通过选项

    -XX:SoftRefLRUPolicyMSPerMB=来控制。SoftRefLRUPolicyMSPerMB选项的作用是,如果堆的空闲内存每增加1MB,soft reference的存活时间就增加毫秒。

    因此,JVM的server模式和SoftRefLRUPolicyMSPerMB会影响GC,我们分析GC问题时可以看看是不是跟这2项因素有关系。

    4) Class Metadata

    在Java中,类卸载也是通过GC完成的,因此类加载/卸载相关的因素也会影响GC。JVM管理class metadata的内存空间,在JDK 8之前class metadata被分配在永久代中,而JDK 8把永久代取消了,class metadata被放在JVM的native memory中,并且class metadata占用的native内存大小,默认不受限制,但是可以通过JVM选项MaxMetaspaceSize加上限制。

    如果通过MaxMetaspaceSize设置了限制,类需要加载而此时内存已达到限制,将引发OOM(OutOfMemoryException)异常。

    就跟对象回收一样,垃圾回收器会随时检查类有没有被使用,判断是否要回收class metadata空间。当垃圾类所占空间达到了一个高水位 (a high-water mark)时,触发一次GC,在GC过程中类卸载掉,然后调整水位。升高还是降低水位取决于从class metadata释放了多少空间、MaxMetaspaceFreeRatio选项,以及MinMetaspaceFreeRatio选项。如果释放的class metadata空间除以总的class metadata空间大于MaxMetaspaceFreeRatio,则降低水位;如果小于MinMetaspaceFreeRatio,则升高水位,水位值的调整影响GC的触发。

    通过MetaspaceSize选项把class metadata空间的初始值尽可能设置的大一些,可以减少因为class unloading而导致的GC。在我们实际项目中,一般没人关注class metadata,可能都是做业务应用的,class metadata空间一般不大,而且不怎么变化吧。

    这个知识比较冷门,适合深入钻研的鞋童。

    再者,如果通过JVM选项

    UseCompressedClassesPointers开启了类指针压缩,那么class metadata空间还要给压缩后的类指针使用,因此class metadata的内存区域就分成了2部分。

    附:JDK 8中JVM怎么管理class metadata内存?

    当class metadata空间不够用时,JVM调用mmap系统调用(而不是malloc)从OS申请内存,然后把这些内存分为 chunk,再把chunk绑定到各个class loader,class loader再使用或归还这些chunk。

    希望这篇文章对你有帮助!如果对互联网、前/后/客户端、架构/分布式/高可用/高并发/高实时、电商、Redis、MySQL、Zookeeper、Spring、Android、浏览器插件、Java、Java虚拟机、C/C++、Linux、个性化推荐、社区发现、机器学习、数据挖掘等感兴趣,欢迎关注且私信。

    相关文章

      网友评论

          本文标题:你还不知道的影响Java GC的其他考虑因素的知识点

          本文链接:https://www.haomeiwen.com/subject/fdtbxrtx.html