Log4j2分析与实践-无垃圾地写日志

    垃圾收集停顿是应用延迟的常见原因,技术人员也在许多系统上对此付出了巨大努力。
    许多日志框架(包括Log4j之前的版本)会在稳定打印日志期间创建很多对象,比如日志事件对象、字符串、字符数组、字节数组等。这就导致了垃圾收集器的压力增加,也增加了GC停顿的发生频率。
    从2.6开始,Log4j在默认在"无垃圾"模式下运行,虽然并非是没有垃圾产生,但不再使用ThreadLocal字段。ThreadLocal字段如果持有非JDK类的话,在Web应用取消部署后,当应用服务器的线程池继续引用这些字段时,可能会导致内存泄漏。当Log4j检测到运行在Web应用下时,"无垃圾"模式是默认的。当然也是可以关闭"无垃圾"模式的。

配置

    Log4j 2.6中的无垃圾写日志特性,一部分通过重用ThreadLocal字段中的对象,也有一部分是通过在将文本转换为字节时复用缓冲。
禁用无垃圾模式
    有两个独立的系统属性可以人工控制Log4j使用的机制来避免创建临时对象:
(1)log4j2.enable.threadlocals,非Web应用默认为true,对象会被存储在ThreadLocal字段中,并且被复用,否则就会为每个日志事件创建新的对象。
(2)log4j2.enable.direct.encoders,默认为true,日志事件先被转换为文本,然后被转换为字节,复用缓冲,不会创建临时对象。
(3)ThreadContext Map默认不是无垃圾模式的,但是从Log4j 2.7开始,可以通过设置log4j2.garbagefree.threadContextMap为true来使其为无垃圾的。
    上面的属性均可在classpath下的log4j2.component.properties文件中设置。

支持的Appenders

    下面的Appender在稳定写日志期间都是无垃圾的:
(1)Console
(2)File
(3)RollingFile(在日志切换时会有一些临时对象生成)
(4)RandomAccessFile
(5)RollingRandomAccessFile(在日志切换时会有一些临时对象生成)
(6)MemoryMappedFile
以上列表中没有出现的Appender都会在稳定写日志期间创建临时对象。

支持的过滤器

(1)CompositeFilter (为了线程安全,添加或移除元素时会创建临时对象)
(2)DynamicThresholdFilter
(3)LevelRangeFilter (从2.8开始)
(4)MapFilter (从2.8开始)
(5)MarkerFilter (从2.8开始)
(6)StructuredDataFilter (从2.8开始)
(7)ThreadContextMapFilter (从2.8开始)
(8)ThresholdFilter (从2.8开始)
(9)TimeFilter (从2.8开始)
    其它过滤器就不是无垃圾的。

支持的Layout

(1)GelfLayout(compressionType="OFF"且没有额外字段包含${')
(2)PatternLayout(仅有部分转换模式是无垃圾的,字段宽度等格式修饰符也是无垃圾的)

API的变化

    用户自定义的实现了CharSequence接口的对象,可以被无垃圾地打印出来:Log4j会将CharSequence对象以StringBuilder的append方式转换为text,避免了在这些对象上调用toString。
    另一种方式是实现org.apache.logging.log4j.util.StringBuilderFormattable接口,那么就可以调用formatTo方法来代替toString方法。
当无垃圾模式被禁用的时候,Log4j也许会直接调用对象的toString方法。

应用代码影响:自动装箱

    无垃圾模式在普通情况下是不会影响到应用代码的,但有个例外。当打印基本类型(比如int和double等)数据时,JVM自动装箱这些基本类型为对象,创建了垃圾。