Monday, July 22, 2024
HomeJavaAsynchronous Logging in Corretto 17

Asynchronous Logging in Corretto 17

Whereas working with an AWS service staff to diagnose surprising Rubbish Assortment (GC) pauses, the Amazon Corretto staff found that purposes had been being throttled by I/O blocking whereas writing logs to disk. In consequence, we determined to implement and contribute async-logging(JDK-8229517)) to OpenJDK 17. On this publish, we’re going to elucidate how you should utilize the -Xlog:async swap and the Unified Logging (UL) framework to keep away from prolonged GC pauses because of I/O. Subsequent to our adjustments, the service improved their outlier p99.99 GC pause latency from 3s to 1s.

UL is a logging framework for the Java Digital Machine (JVM) launched in OpenJDK 9. Many AWS companies detect anomalies and subsequently take motion primarily based upon logging output from working JVMs. Logs additionally present ample particulars about Rubbish Assortment (GC) actions, serving to builders to pinpoint the reason for lengthy pauses and tune GC efficiency primarily based on them.

UL is versatile in which you can change its configuration at runtime. OpenJDK customers can cut back their logging overhead and save disk house with a default, terse configuration, however can dynamically enhance logging output when required. For instance, a developer can map an alarm set off to code that may ask the JVM to extend logging context and verbosity when the monitored JVM crosses a given threshold. Some AWS companies use this sample to cut back default operational hundreds.

You possibly can entry these options utilizing any OpenJDK distro, together with Amazon’s Corretto 17. To know how this works, let’s dive into particulars.

JVM Unified Logging

Unified Logging (UL) is a dynamic, tag-based logging framework for the HotSpot JVM. UL configuration is a mini language unto itself. OpenJDK customers will discover a formal syntax in JEP-158 and a assist message utilizing -Xlog:assist, however we’ll present the fundamental ideas right here.

As of OpenJDK 17, builders can entry lots of of log tags that they will use to determine logging information output. Some examples of the obtainable tag names are class, compilation, gc, metadata, and stats. These signify the item of the logging data. You will discover a full checklist of those by working the assistance command famous above.

Subsequent, you’ll be able to group these tags, with the intention to modify the logging ranges related to a number of logging tags concurrently. We name this grouping a tagset. A developer will doubtless see the worth in having the ability to flip up the logging degree for a variety of associated tags.

Then, we now have the logging instrumentation. Let’s name these “log websites”. Every log website is logically related to one tagset. Lastly, we now have a log selector, which is a question string you should utilize to match from zero to a number of tagsets together with their verbosity ranges. It’s the log selector that you should utilize to filter the logging output.

Right here an instance, the log message are obtained from java -Xlog:'gc*=data:stdout'

[0.030s][info][gc,init] CardTable entry dimension: 512
[0.050s][info][gc ] Utilizing G1
[0.068s][info][gc,init] Concurrent Employees: 7
[0.068s][info][gc,metaspace] Slender klass base: 0x0000000800000000, Slender klass shift: 0, Slender klass vary: 0x100000000

The primary log message comes from the log website under; its tagset is (gc,init). The code will solely be executed when the log is chosen.

log_info_p(gc, init)("CardTable entry dimension: " UINT32_FORMAT, _card_size);

The second log message has a logset ‘gc’.

log_info(gc)("Utilizing %s", _collectedHeap→title());

‘gc=data’ solely selects logsites with the tagset ‘gc’. In contrast, the wildcard is used to match all tagsets. 'gc*' will choose tagset ‘gc’, ‘gc+init’ and all others with prefix gc. The log selector gc*=data' within the instance breaks down into 2 components: question issues ‘gc*’ and verbosity degree ‘data’.

This weblog publish just isn’t a radical tutorial of UL. Readers who should not aware of it could actually learn JEP-158 or this tutorial. Since OpenJDK 9, there’s been no separate GC log: it was built-in into UL. Should you’ve used OpenJDK 8, you will have added XX:+PrintGC to your command line. Since OpenJDK 9, you’ve been in a position to choose GC logs utilizing the “gc” tag together with others. Extra particulars may be present in JEP-271.


Asynchronous logging

You possibly can direct output UL to a file. Although file I/O is buffered, log writing nonetheless can’t be assured to be non-blocking as a result of the ‘write()’ syscall is determined by the implementation of a selected filesystem and the underlying bodily gadget. E.g., Linux software program RAID must synchronize a number of writes to completely different disk partitions, and a network-backed Digital File System (VFS) could block whereas writing because of a sluggish community connection.

The HotSpot JVM has a worldwide synchronization mechanism known as a ‘safepoint’. At a safepoint, HotSpot forces all Java threads to pause. Some GC-related VM operations require a safepoint to make sure that Java threads don’t mutate the Java heap whereas GC is working. If the UL writes to information throughout a safepoint and the writes occur to be delayed or blocked, then the safepoint pause time for such VM operations will likely be extended. These low-level incidents will enhance utility response time.

One sample that ought to draw your consideration to potential I/O issues is an uncommon CPU time distribution. The gc,gc+cpu tagset reveals such data. Within the instance under, we ran the Java utility with 7 concurrent GC threads. We’d anticipate that the “Consumer” time related to the thread exercise can be greater than the “Actual” time. Nonetheless, within the log we see that the “Consumer” time is definitely smaller, which makes us suspicious. One potential clarification for this might be that some GC threads had been blocked by disk I/O. Maybe they weren’t within the wait queue so they’d not eat CPU time.

[gc     ] GC(28) Pause Younger (Combined) (G1 Evacuation Pause) 8200M->7105M(10240M) 770.84ms
[gc,cpu ] GC(28) Consumer=0.36s Sys=0.02s Actual=0.77s

To handle the problem, the AWS Corretto Staff developed a brand new characteristic known as “Asynchronous Logging” (asynclog), and have added it to OpenJDK 17. When asynclog is enabled, logsites enqueue logging messages to an in-memory buffer and a devoted, concurrent thread asynchronously flushes them to the desired output file. Log writes to the in-memory buffer are assured to be non-blocking. By default, the intermediate buffer dimension is bounded to 2 MB, however configurable with -XX:AsyncLogBufferSize. If the buffer fills up earlier than the asynchronous log author thread can dump its contents to file, new log messages will likely be discarded. You need to use the brand new choice -Xlog:async to inform UL to run in asynclog mode.

The next two experiments present why asynclog issues for Java purposes. The primary reveals the way it alleviates the logging penalty by leveraging concurrency. The second reveals how asynclog improves high-percentile latency.

Efficiency affect on Java HelloWorld with full UL

Within the first experiment, we allow all=hint to provide exhaustive UL logs. This tagset covers all components and your entire JVM lifecycle. For a typical “HelloWorld” program working on OpenJDK 17, after we enabled this selection, we noticed 16,219 messages for synchronous UL and 16,225 log messages for -Xlog:async. The additional log messages are from the asynclog subsystem itself. Please word that filecount=0 is a UL output choice which prevents log file rotation. Right here is the complete Java command.

java -Xlog:async -Xlog:all=hint:file=all.log::filecount=0 HelloWorld

As you’ll be able to see within the graph, in our experiment, asynchronous logging diminished the Actual CPU time by 34.5% from 108 to 70.7ms. On the similar time, we noticed general CPU utilization enhance from 102.7% to 171.6%. The runtime acquire is achieved by offloading the I/O process of log writing right into a separate, concurrent thread. Whereas this significantly reduces absolutely the working time of the appliance it additionally will increase general CPU consumption because of overhead launched by the extra thread.

Default vs. Async Logging Graph showing a reduction in clock time for Async and showing that there's a corresponding increase in CPU untilization.

Influence on high-percentile GC pauses

In a second experiment we ran the benchmark heapothesys/HyperAlloc on a c5d.2xlarge occasion which was backed by Exhausting Disk Drives (HDD), that are far slower than SSD. To make the latency extra apparent, we used a background script to make sure disk utilization was near 100%. We chosen all GC tagsets with ‘debug’ verbosity. Our take a look at program created 4 threads with an allocation fee of 1024M/s for 180 seconds and created a gc.log output file of about 4MB.

java -Xlog:async -Xlog:'gc*=debug:file=gc.log::filecount=0' -javaagent:jHiccup.jar="-a -d 10000 -i 1000 -l asynclog.log" -jar HyperAlloc-1.0.jar -d 180

The information present how G1 GC latency is affected by disk I/O at p99.9 when GC logs are written synchronously. Some latency outliers (crimson spikes), aka Hiccups, make the high-percentile GC pause time skyrocket. Asynclog is efficient at curbing the affect of disk I/O, so even at p99.999 the latency stays under 15ms.

Maximum Latency Intervals, Default vs. Async Logging, showing large spikes in latency for Default and relatively flat latency for Async. And Latency by Percentile Distribution, Default vs. Async Logging, showing a log-like curve for Default and a gradual slope for Async

Dynamic Configuration

OpenJDK customers can change UL configuration on the fly even when they don’t arrange UL arguments on the Java command line. jcmd is a standalone instrument which sends diagnostic instructions (dcmds) to HotSpot. HotSpot has a thread known as AttachListener, which listens on an area socket and processes incoming dcmds. For UL, the VM.log dcmd can show the present UL settings and alter them. The next desk reveals the arguments of VM.log.

VM.log Description Be aware
output The title or index (#<index>) of output to vary. UL will create a brand new output if it’s new. in any other case, it simply updates ‘what’ and ‘decorators’ for the prevailing one.
output_options Choices for the output. cannot change for an current output.
what Log selector for the output
decorators Decorators for the output. Use ‘none’ or an empty worth to take away all.
disable Subcommand: disable all unifed logging outputs
checklist Subcommand: checklist present log configuration
rotate Subcommand: rotate all logs

Assuming you will have a Java utility began whose PID is 85254. You need to use VM.log so as to add a brand new log output.

$jcmd 85254 VM.log output=gc.log what=gc=debug decorators=uptime,pid,tags,degree
Command executed efficiently

gc=debug instructs UL to pick log entries with the tag gc and a verbosity that’s equal or greater than debug. The output goes to the file gc.log. When this dcmd completes, you’ll begin observing the GC log within the file. You need to use the checklist sub-command to confirm that the log output has been up to date.

$jcmd 85254 VM.log checklist
Log output configuration:
#0: stdout all=warning,gc=debug uptime,pid,degree,tags (reconfigured)
#1: stderr all=off uptime,degree,tags
#2: file=gc.log all=off,gc=debug uptime,pid,degree,tags filecount=0,filesize=20480K,async=false (reconfigured)

Java customers can enhance the log verbosity or develop the tagset if extra data is required. E.g.,

$jcmd 85254 VM.log output=#2 what="gc*=hint"

output=#2 refers back to the prior output file gc.log. gc* is a log selector with a wildcard. It matches all tagsets with gc, which is broader than earlier than. In the meantime, the verbosity degree has elevated from debug to hint. Here’s a pattern of logs after this adjustment.

[34.844s][debug][gc,heap              ] GC(90) Heap after GC invocations=91 (full 0):
[34.844s][debug][gc,heap              ] GC(90)  garbage-first heap   complete 10485760K, used 6873044K [0x0000000580000000, 0x0000000800000000)
[34.844s][debug][gc,heap              ] GC(90)   area dimension 8192K, 14 younger (114688K), 14 survivors (114688K)
[34.844s][debug][gc,heap              ] GC(90)  Metaspace       used 4685K, dedicated 4864K, reserved 1056768K
[34.844s][debug][gc,heap              ] GC(90)   class house    used 505K, dedicated 640K, reserved 1048576K
[34.844s][info ][gc                   ] GC(90) Pause Younger (Regular) (G1 Evacuation Pause) 7255M->6711M(10240M) 185.667ms
[34.844s][info ][gc,cpu               ] GC(90) Consumer=0.19s Sys=0.25s Actual=0.18s
[34.844s][trace][gc,region            ] G1HR ALLOC(EDEN) [0x00000007fe800000, 0x00000007fe800000, 0x00000007ff000000]
[34.844s][trace][gc,task              ] G1 Service Thread (Remembered Set Sampling Process) (schedule) @35.130s
[34.844s][trace][gc,alloc             ] Thread-0: Efficiently scheduled assortment returning 0x00000007fe800000
[34.844s][debug][gc,task              ] G1 Service Thread (Remembered Set Sampling Process) (run) 82.982ms (cpu: 82.979ms)
[34.844s][trace][gc,tlab              ] ThreadLocalAllocBuffer::compute_size(3) returns 524288
[34.844s][debug][gc,task,start        ] G1 Service Thread (Periodic GC Process) (run)
[34.844s][trace][gc,tlab              ] TLAB: fill thread: 0x00007fd24c016c00 [id: 38147] desired_size: 4096KB sluggish allocs: 0  refill waste: 65536B alloc: 0.99843     8179KB refills: 1 waste  0.0% gc: 0B sluggish: 0B

Java builders also can management UL programmatically proper from their utility as a result of the dcmd performance has been uncovered as a MXBean. Initially, Java purposes must allow JMX.

The next process reconfigures output #2 by way of JMX. The impact is similar because the prior jcmd command. Please word that authentication and SSL are ignored for simplicity. That’s not a superb follow, however it is a demo.

import javax.administration.MBeanServerConnection;
import javax.administration.ObjectName;
import javax.administration.distant.*;

public class CrankUpGCLog {
    public static void fundamental(String[] args) throws Exception {
        JMXServiceURL url =
                new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.join(url, null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        ObjectName mxbean = new ObjectName("");

        String params[] = {"output=#2", "what=gc*=hint", "decorators=uptime,pid,tags,degree"};
        mbsc.invoke(mxbean,"vmLog", new Object[]{params}, new String[]{String[].class.getName()});

Testing it your self

Should you’d wish to experiment your self with Async logging, you’ll be able to comply with the directions under.  Please guarantee you will have Corretto 17 or later put in, because the Asynchronous logging characteristic was added in Corretto 17.

java -version

openjdk model "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Atmosphere Corretto- (construct 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM Corretto- (construct 17.0.1+12-LTS, combined mode, sharing)

Now write HelloWorld Java Software in

Home windows (In Powershell, create a time perform to measure the elapsed time it takes to execute the appliance)

#Create the next perform in Home windows Powershell
#Do not do that for Linux.  The 'time' perform is already inbuilt
PS c:Usersyour_name>perform time { $Command = "$args"; Measure-Command  out-default }

On MacOS, Linux, and Home windows (Utilizing Powershell)

Please word that the output under was obtained by way of Home windows Powershell.

# Check the Software with out utilizing Async Logging
# You possibly can run it just a few instances to get a median
# NOTE: in Home windows Powershell, go within the parameters in single quotes 
# Please take away the only quotes for Linux implementations

 PS C:UsersAdministrator> time java '-Xlog:all=hint:file=hotspot.log.1:l,tg:filecount=0' HelloWorld
 Hi there World!

Days : 0
Hours  : 0
Minutes  : 0
Seconds  : 0
Milliseconds : 514
Ticks  : 5149368
TotalDays  : 5.95991666666667E-06
TotalHours : 0.000143038
TotalMinutes : 0.00858228
TotalSeconds : 0.5149368
TotalMilliseconds : 514.9368
# Now Check the Software with Async Logging
# NOTE: In Home windows Powershell, go within the parameters in single quotes 
# Please take away the only quotes for Linux implementations
PS C:UsersAdministrator>time java '-Xlog:async -XX:AsyncLogBufferSize=4M -Xlog:all=hint:file=hotspot-async.log:l,tg:filecount=0' HelloWorld
 Hi there World!

Days : 0
Hours  : 0
Minutes  : 0
Seconds  : 0
Milliseconds : 401
Ticks  : 4015190
TotalDays  : 4.64721064814815E-06
TotalHours : 0.000111533055555556
TotalMinutes : 0.00669198333333333
TotalSeconds : 0.401519
TotalMilliseconds : 401.519

You’ll need to run the above experiment a variety of instances to get a superb common.


This weblog launched a brand new Unified Logging options in OpenJDK and Corretto 17. Asynchronous logging reduces utility pauses because of UL by decoupling logging from disk I/O. Dynamic configuration, which has been obtainable since OpenJDK 9, gives true on-demand logging to keep away from the fixed overhead of UL till it’s actually wanted. They’re orthogonal, so Java customers can use them independently.

In regards to the authors

Xin Liu

Xin Liu is a Senior Software program Engineer centered on the Corretto Java Improvement Equipment. He has a ardour for bettering Corretto and OpenJDK. He’s situated within the Seattle space. You will discover him on Twitter at @navyasm.

Mike Cook dinner

Mike Cook dinner is a Principal Product Supervisor centered on Corretto. He wish to enhance the Java Developer and Operations experiences. He’s situated in New Jersey. You will discover him on Twitter at @correttoMike.



Please enter your comment!
Please enter your name here

Most Popular

Recent Comments