首页
留言
动态
归档
推荐
音乐
工具
Search
1
Emby公益服-上万部电影电视剧免费看
68,109 阅读
2
openwrt-docker部署lxk0301京东自动签到脚本
13,008 阅读
3
QuantumultX-京东签到撸京东豆
11,249 阅读
4
LXK0301京东签到脚本-自动提交互助码
9,699 阅读
5
微信-域名被封监测以及自动更换被封域名
9,215 阅读
随便写写
科学上网
Web开发
瞎折腾
Search
标签搜索
quantumultx
laravel
openwrt
laravel nova
laradock
telegram
DDC/CL
薅羊毛
google adsense
jd_scripts
京东签到
ubuntu
oh-my-zsh
web开发环境
nginx
工具
shadowsocks shadowsocksR
RBAC
权限管理
内网穿透
orzlee
累计撰写
46
篇文章
累计收到
596
条评论
首页
栏目
随便写写
科学上网
Web开发
瞎折腾
页面
留言
动态
归档
推荐
音乐
工具
搜索到
1
篇与
JAVA
的结果
2024-05-06
JAVA RMI GDC每小时触发Full GC
前言 最近倒腾Java,发现JVM并行收集器在还有大量堆内存的时候就开始触发老年代的回收,通过GC日志发现每小时都会执行GC和Full GC回收,回收是通过System.gc()调用。 那每小时都调用一次Full GC,我添加启动参数-Xms11g -Xmx11g分配11G堆内存只是为了好看吗?每次Full GC耗时在600ms,并行收集器老年代内存使用还不足5%,真是发神经了! 过程就不做记录了,无聊又蛋疼。以下是在使用了RMI或者其他NIO对象的情况。 RMI DGC每小时触发一次Full GC 原因:DGC代码中调用System.gc()导致并行收集器触发Full GC。 解决办法: 修改DGC调用System.gc()执行时间,默认一小时,单位ms。启动参数添加: -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 禁止程序中的System.gc(),添加启动参数: -XX:+DisableExplicitGC 使用支持并发的收集器(CMS、G1、ZGC或Shenandoah),并且添加启动参数: -XX:+ExplicitGCInvokesConcurrent 方法1,治标不治本,延长时间触发Full GC会导致STW时间更长,可以配合并行收集器使用-XX:+ExplicitGCInvokesConcurrent来控制频率。 方法3,启用-XX:+ExplicitGCInvokesConcurrent参数可以尝试在垃圾收集时使用并发收集来减少STW时间,从而提高应用程序的响应性。 不建议使用-XX:+DisableExplicitGC来限制System.gc()。 因为从Java 7开始,Java提供了基于NIO的RMI实现,称为NIO-based RMI。这种实现利用了NIO的非阻塞I/O模型,能够更高效地处理大量的并发连接,减少了线程资源的消耗,并提高了性能和可伸缩性。通过使用NIO,NIO-based RMI能够更好地适应高并发和大规模的网络通信场景。 DirectByteBuffer是一种在堆外内存中分配的ByteBuffer,它与NIO密切相关,因为它可以直接映射到操作系统的内存中,从而避免了在Java堆和本地内存之间进行复制。在NIO编程中,使用DirectByteBuffer可以提高I/O操作的性能和效率,特别是在处理大量数据时。在基于NIO的RMI实现中,使用NioServerSocketChannel和NioEventLoopGroup通常涉及到DirectByteBuffer的使用。 虽然 DirectByteBuffer 对象本身是由 JVM 管理的,它们存储在 Java 堆内存中,但是 DirectByteBuffer 对象所持有的实际数据存储在堆外内存中。 JVM 通过 DirectByteBuffer 对象来管理对堆外内存的访问和操作。当 DirectByteBuffer 对象被垃圾回收时,它所持有的堆外内存也会随之被释放。 因此,触发 Full GC 通常是为了回收已经失去引用的 DirectByteBuffer 对象,进而释放掉它们所占用的堆外内存。 Full GC 在回收过程中会扫描整个堆内存,包括其中的对象和引用。当发现 DirectByteBuffer 对象已经没有引用指向时,JVM 就会将其标记为可回收, 待下次 Full GC 执行时进行回收。这样,间接地通过 Full GC 回收了 DirectByteBuffer 对象,也就释放了相应的堆外内存。 如果禁用了System.gc(),那么不会及时的清理 DirectByteBuffer 或者其他 NIO 对象导致堆外内存也不会释放。虽然最后也可能因为JVM堆内内存不足触发Full GC 来释放,但没必要冒险。参阅 监控程序调用System.gc()堆栈 可以使用 async-profiler 跟踪System.gc调用者: 预先开始分析: profiler.sh start -e java.lang.System.gc <pid> 发生一种或多种System.gc情况后,停止分析并打印堆栈跟踪: --- Execution profile --- Total samples : 6 Frame buffer usage : 0.0007% --- 4 calls (66.67%), 4 samples [ 0] java.lang.System.gc [ 1] java.nio.Bits.reserveMemory [ 2] java.nio.DirectByteBuffer.<init> [ 3] java.nio.ByteBuffer.allocateDirect [ 4] Allocate.main --- 2 calls (33.33%), 2 samples [ 0] java.lang.System.gc [ 1] sun.misc.GC$Daemon.run 在上面的示例中,System.gc从两个地方调用了 6 次。这两种情况都是 JDK 内部强制进行垃圾回收的典型情况。第一个来自java.nio.Bits.reserveMemory.当没有足够的可用内存来分配新的直接 ByteBuffer 时(由于-XX:MaxDirectMemorySize限制), JDK 会强制进行 Full GC 回收无法访问的直接 ByteBuffer。 第二个来自 GC Daemon 线程。这由 Java RMI 运行时定期调用。例如,如果您使用 JMX 远程,则每小时自动启用一次定期 GC。 这可以 通过-Dsun.rmi.dgc.client.gcInterval系统属性进行调整。 结语 起初我很不明白为什么DGC代码里面会有System.gc(),我一直以为堆外内存不归JVM管理,那调用System.gc()又有什么用呢?又是问ChatGPT又是各种搜索,才发现虽然JVM不管理堆外内存,但是管理使用堆外内存的对象,System.gc()是为了及时释放掉不再使用的堆外内存持有对象,堆内释放了,操作系统就会释放堆外内存了!
2024年05月06日
68 阅读
0 评论
0 点赞