Nacos Client 2.4.2 日志重载导致大量 ClassUnload 事件
环境信息
- Nacos Client 版本: 2.4.2
- 旧版本: 2.1.2(无此问题)
- JDK 版本: 21.0.6
- 日志框架: Log4j2
- 监控工具: Java Flight Recorder (JFR)
问题描述
将 Nacos Client 从 2.1.2 升级到 2.4.2 后,我们观察到 ClassUnload 事件数量显著增加。
观察到的现象
| 指标 |
2.1.2 版本 |
2.4.2 版本 |
影响 |
| ClassUnload 事件 |
极少 |
每 2 小时左右 300-400 个 |
⬆️ 显著增加 |
| 来源线程 |
无 |
com.alibaba.nacos.client.logging.0 |
98.6% 的新加载 |
根因分析
通过详细的 JFR 数据分析,我们确定了 2.4.2 版本引入的 Nacos 日志重载机制 是导致此问题的根本原因:
- 定期检查: 日志线程默认每 10 秒检查一次配置变化
- 频繁重载: 不知为何,重载大约每 1-2 小时触发一次(即使日志配置文件从未被修改)
- LambdaForm 生成: 每次重载都会触发 Log4j2 插件系统重新初始化,生成 150-200 个新的
java.lang.invoke.LambdaForm 类
- Metaspace GC: 这些类不断累积,最终在每 2 小时左右的 Metaspace GC 时被批量卸载
源码引用
源码位置: com/alibaba/nacos/client/logging/NacosLogging.java
问题代码(v2.4.2)
// NacosLogging.java - 第 82-91 行
private void scheduleReloadTask() {
ScheduledExecutorService reloadContextService = ExecutorFactory.Managed
.newSingleScheduledExecutorService("Nacos-Client",
new NameThreadFactory("com.alibaba.nacos.client.logging"));
reloadContextService.scheduleAtFixedRate(() -> {
if (loggingAdapter.isNeedReloadConfiguration()) {
loggingAdapter.loadConfiguration(loggingProperties);
}
}, 0, loggingProperties.getReloadInternal(), TimeUnit.SECONDS);
}
默认间隔: NacosLoggingProperties.DEFAULT_RELOAD_INTERNAL_SECONDS = 10(秒)
调用堆栈(来自 JFR)
线程: com.alibaba.nacos.client.logging.0
com.alibaba.nacos.client.logging.NacosLogging.lambda$scheduleReloadTask$0(NacosLogging.java:88)
↓
com.alibaba.nacos.logger.adapter.log4j2.Log4J2NacosLoggingAdapter.loadConfiguration
↓
org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:319)
↓
org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.injectFields(PluginBuilder.java:198)
↓
java.lang.reflect.Field.set(Field.java:836)
↓
java.lang.invoke.MethodHandle.asType(MethodHandle.java:870)
↓
java.lang.invoke.LambdaForm.compileToBytecode(LambdaForm.java:849)
↓
生成: java.lang.invoke.LambdaForm$MH+0x.../[identityHash](每次重载 150-200 个类)
需要确认的问题
1. 根因确认
问: 能否确认 NacosLogging.scheduleReloadTask() 中的定期日志重载机制是导致频繁 ClassLoad/Unload 事件的根本原因?
我们的发现:
- 98.6% 的新 ClassUnload的类 是来自
com.alibaba.nacos.client.logging.0 线程创建的
- 重载大约每 1-2 小时触发一次,即使日志配置文件从未被修改
- 每次重载由于 Log4j2 插件重新初始化会生成 150-200 个新的 LambdaForm 类
2. 是否可以默认禁用此功能
问: 是否可以默认禁用此功能,或者额有什么配置关闭此功能(配置文件中的配置)?
当前问题:
设置 -Dnacos.logging.reload.interval.seconds=-1 会导致启动失败:
java.lang.IllegalArgumentException
at java.util.concurrent.ScheduledThreadPoolExecutor.scheduleAtFixedRate(ScheduledThreadPoolExecutor.java:623)
at com.alibaba.nacos.client.logging.NacosLogging.scheduleReloadTask(NacosLogging.java:86)
at com.alibaba.nacos.client.logging.NacosLogging.initLoggingAdapter(NacosLogging.java:66)
at com.alibaba.nacos.client.logging.NacosLogging.<init>(NacosLogging.java:46)
我们的临时解决方案:
-Dnacos.logging.reload.interval.seconds=31536000 # 1 年(实际上相当于禁用)
此方案成功阻止了频繁的 ClassLoad/Unload, 是否可以默认关闭动态加载配置呢?
感谢您关注此问题。我们非常感谢 Nacos 项目,期待您的解决方案! 🙏
Nacos Client 2.4.2 日志重载导致大量 ClassUnload 事件
环境信息
问题描述
将 Nacos Client 从 2.1.2 升级到 2.4.2 后,我们观察到 ClassUnload 事件数量显著增加。
观察到的现象
com.alibaba.nacos.client.logging.0根因分析
通过详细的 JFR 数据分析,我们确定了 2.4.2 版本引入的 Nacos 日志重载机制 是导致此问题的根本原因:
java.lang.invoke.LambdaForm类源码引用
源码位置:
com/alibaba/nacos/client/logging/NacosLogging.java问题代码(v2.4.2)
默认间隔:
NacosLoggingProperties.DEFAULT_RELOAD_INTERNAL_SECONDS = 10(秒)调用堆栈(来自 JFR)
需要确认的问题
1. 根因确认
问: 能否确认
NacosLogging.scheduleReloadTask()中的定期日志重载机制是导致频繁 ClassLoad/Unload 事件的根本原因?我们的发现:
com.alibaba.nacos.client.logging.0线程创建的2. 是否可以默认禁用此功能
问: 是否可以默认禁用此功能,或者额有什么配置关闭此功能(配置文件中的配置)?
当前问题:
设置
-Dnacos.logging.reload.interval.seconds=-1会导致启动失败:我们的临时解决方案:
-Dnacos.logging.reload.interval.seconds=31536000 # 1 年(实际上相当于禁用)此方案成功阻止了频繁的 ClassLoad/Unload, 是否可以默认关闭动态加载配置呢?
感谢您关注此问题。我们非常感谢 Nacos 项目,期待您的解决方案! 🙏