Log4j 2.6, the latest version of the popular logging library for Java, will include a number of configuration options that allows it to run in a completely garbage-free manner. The release follows previous attempts to improve the performance of logging libraries, and has been positively received by the industry. As indicated by Remko Popma, lead of the initiative, next steps will include increasing the number of scenarios where log4j can run GC-free.
In July 2014, log4j 2.0 revolutionised the logging frameworks landscape by introducing asynchronous loggers, providing 6 to 68 times higher throughput compared to their synchronous counterparts. Impressive as these results may be, logging frameworks were still responsible for a large proportion of the response time of a number of high-throughput, low-latency applications, which frequently forced developers to exclude them in their deployments. Since a significant part of the effort spent in fine-tuning this type of high-performant applications goes to avoiding GC pauses, the log4j team concluded that these improvements would allow more people to use log4j. Judging by comments from performance expert and Java Champion Kirk Pepperdine, the assumption seems to be right:
Logging in Java is in a very sad state. To date, I’ve rarely run into a customer whose system wasn’t somehow negativity impacted by logging. My extreme case is a customer that was facing a 4.5 seconds time budget where logging accounted for 4.2 seconds (big portion was pressure on the async appender). I will be looking with great interest at this release.
Preventing garbage collections is achieved by avoiding the creation of temporary objects, which in turn means reusing existing objects as much as possible. However, in this first release, the entire library could not be made GC-free, and therefore developers who wish to benefit from this capability need to be aware of the restrictions around appenders, loggers, formatting layouts, and API usage.
Application types
Objects are partially reused by storing them in ThreadLocal fields. This works fine for standalone applications, but can cause memory leaks in web applications; the application server may keep the ThreadLocal in a thread pool, meaning objects intended for logging are still being referenced after the application has been undeployed. For this reason, the capability to reuse objects through ThreadLocal is disabled by default in web applications, and therefore log4j cannot completely run GC-free.
Loggers
Another way log4j prevents garbage collections is by reusing buffers when converting text to bytes. All applications can benefit from this behaviour, and it's in fact enabled by default. However, multithreaded applications that use synchronous loggers may experience some performance impact as the different threads compete for the shared buffer; if this is an issue, asynchronous loggers should be used or the option to share buffers disabled.
Appenders
Only a subset of appenders have been modified to avoid creation of temporary objects: Console, File, RandomAccessFile, their rolling counterparts, and MemoryMappedFile. Any other appender will create garbage that needs to be collected. However, it has to be noted that although these appenders can operate garbage-free, there may be other I/O-related factors that affect their performance.
Formatting layouts
Formatting layouts probably represents the trickiest part the developer will have to handle when attempting to achieve GC-free logging, due to the fact that they'll have to worry not only about the layouts to use, but also the options within those layouts. GelfLayout is supported only if compression is disabled, while PatternLayout works garbage-free only if using a restricted set of conversion patterns; any other conversion pattern will create temporary objects that will have to be collected.
API usage
The API itself has been modified to avoid creation of temporary objects. This way, instead of having methods that accept a simple vararg parameter to support any number of parameters (and which creates a temporary array), log4j now includes overloaded versions of all methods for up to ten arguments; calling methods with more than ten parameters will still use vararg and therefore create a temporary array.
This restriction is more acute if using log4j through SLF4J, since the facade library only includes non-varargs methods for up to two parameters. Users who wish to use more than two parameters and still run garbage-free will have to drop SLF4J.
Impact to code
Although care has been taken to prevent the need for developers to update their code, there is a type of temporary object creation that doesn't depend on log4j: autoboxing of primitive types. To ensure the JVM doesn't wrap up primitives with their object counterparts, developers can use the static method Unbox.box() when passing primitive arguments to log4j. This method will allow log4j to process the primitives without creating unnecessary objects.
Despite the number of apparent limitations, these changes have the potential to significantly improve the logging experience of applications with strict performance requirements. Those who see themselves excluded by the current restrictions should keep an eye at the change backlog, for further improvements may be made available in future releases.