首页 > 解决方案 > 日志记录过于频繁会导致堆大小急剧增加吗?

问题描述

我正在调查 java 进程中发生的内存突然增加。我看到堆大小正在增长到 3-4 GB。经过调查,我发现在课堂上,正在发生日志记录(非常频繁,例如每 1 或 2 毫秒),在 1 秒内产生大约 4MB 的日志。所以根据我的说法,这是堆大小增加的原因。此外,在代码从类中出来并且日志记录停止后,堆大小也不会减少。该过程使用 log4j-1.2.16.jar。

我有以下问题。

  1. log4j 是否因为频繁记录而在堆上创建了太多对象?如果是,这是预期的行为还是 log4j 中的某些限制,在 log4j2 中已解决?
  2. 为什么堆大小没有减少?log4j 是否持有对堆上对象的引用,因为它们没有被 GC 收集?

请帮助我理解 log4j 和 jvm 的这种行为。我不知道这是否是已知的事情,log4j2 已经解决了它。

标签: javamemory-leakslog4jresource-leak

解决方案


Log4j被标记为生命周期结束,因此升级到较新版本是个好主意。写入大量日志文件可能会导致性能问题,具体取决于您使用日志记录 API 的方式,但您仍然应该能够在不升级的情况下解决一些日志记录问题。

查看消息并检查详细程度。一个关键问题是打印太多消息或代码,这些消息或代码不会检查日志记录级别,因为即使不需要消息,也会对字符串表达式进行评估。这可能是高和不必要的内存分配的原因。考虑:

logger.debug("This line causes memory allocations even if not printed: " +value);

如果将日志记录级别更改为WARN,则消息的上述字符串连接仍会执行,如果value.toString()是昂贵的内存操作,除了 JVM 添加的 StringBuilder 调用之外,还会有更多不必要的对象分配。更改您的 log4j 调用以在记录之前检查级别:

if (logger.isDebugEnabled()) {
    logger.debug("The value is: " +value);
}
logger.debug("No need to call logger.isDebugEnabled() for a string literal");

使用 log4j2,您可以使用格式字符串或Supplier<String>进行日志记录,并且仅在需要消息时才评估日志消息的字符串连接。

写入 System.out 可能会很昂贵,因此请确保只有 INFO 消息出现在控制台中并使用附加程序,以便 DEBUG 消息进入日志文件。


推荐阅读