Skip to content

nacos-client从2.1.2升级到2.4.2之后,unload class持续增长 #13940

Description

@willchaofeiyang

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 日志重载机制 是导致此问题的根本原因:

  1. 定期检查: 日志线程默认每 10 秒检查一次配置变化
  2. 频繁重载: 不知为何,重载大约每 1-2 小时触发一次(即使日志配置文件从未被修改)
  3. LambdaForm 生成: 每次重载都会触发 Log4j2 插件系统重新初始化,生成 150-200 个新的 java.lang.invoke.LambdaForm
  4. 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 项目,期待您的解决方案! 🙏

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/ClientRelated to Nacos Client SDKkind/enhancementCategory issues or prs related to enhancement.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions