【问题标题】:Can logging too frequently cause increase in heap size sharply?日志记录过于频繁会导致堆大小急剧增加吗?
【发布时间】:2020-12-14 06:56:20
【问题描述】:
我正在调查 Java 进程中发生的内存突然增加。我看到堆大小正在增长到 3-4 GB。经过调查,我发现在课堂上,正在发生日志记录(非常频繁,例如每 1 或 2 毫秒),在 1 秒内产生大约 4MB 的日志。所以根据我的说法,这是堆大小增加的原因。此外,在代码从类中出来并且日志记录停止后,堆大小也不会减少。该进程正在使用 log4j-1.2.16.jar。
我有以下问题。
- log4j 是否因为频繁记录而在堆上创建了太多对象?如果是,这是预期的行为,还是 log4j 中的某些限制在 log4j2 中得到解决?
- 为什么堆大小没有减小? log4j 是否持有对堆上对象的引用,因为它们没有被 GC 收集?
请帮助我理解 log4j 和 jvm 的这种行为。我不知道这是否是已知的事情,log4j2 已经解决了它。
【问题讨论】:
标签:
java
memory-leaks
log4j
resource-leak
【解决方案1】:
Log4j 被标记为生命周期结束,因此升级到较新版本是个好主意。写入大量日志文件可能会导致性能问题,具体取决于您使用日志记录 API 的方式,但您仍然应该能够在不升级的情况下解决一些日志记录问题。
查看消息并检查详细程度。一个关键问题是打印过多的消息或代码,这些消息或代码不会检查日志记录级别,因为即使不需要消息,也会对字符串表达式进行评估。这可能是高和不必要的内存分配的原因。考虑:
logger.debug("This line causes memory allocations even if not printed: " +value);
如果您将日志记录级别更改为WARN,则消息的上述字符串连接仍会执行,如果value.toString() 是一项昂贵的内存操作,那么除了由JVM。更改您的 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 消息转到日志文件。