Skip to content

warmuuh/allocation-tracker

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

object allocation tracker

Purpose and Scope

The purpose of this agent is to fix excessive object allocation issues. While most profilers do offer allocation tracking, they all struggle heavily when the garbage issue is really out of hand. Examples are for example gigabytes of object allocations per second.

This single purpose agent is extremely lightweight and highly optimized using a Calling Context Tree (CCT) with thread-local buffers (improvement PRs are welcome at any time!).

It is intended to be inserted into production or load test environments and will give you a report showing not just allocation counts, but complete stack traces showing WHERE objects are being allocated.

The agent captures the calling context (stack trace) for each allocation, allowing you to identify the exact code paths responsible for excessive allocations.

The agent does not differentiate between live and garbage objects, issues with live objects can usually be solved with a heap dump, finding out which objects create garbage is usually much harder.

Limitations

By design you have to provide a comma-separated list of specific class names to instrument. The agent now supports instrumenting JDK classes by automatically adding the agent JAR to the bootstrap class loader and enabling retransformation capabilities.

If you create more than Long.MAX_VALUE instances of a tracked class the counter will roll over without you noticing it, but that scenario is unlikely. The agent uses LongAdder for high-performance counter updates under contention (standard in Java 17+ build).

The agent is controlled by a PlatformMBeanServer MBean. This might require activation on certain application servers.

For performance reasons the agent does not try to figure out if a class is a superclass or not. Thus superclasses will be included in the report and common base classes will likely appear at the top.

Build

The agent is built as a multi-release JAR supporting Java 17+ with optimizations for Java 21+.

Build Requirements:

  • JDK 21 (for building)
  • Maven 3.6.3+

Build Command:

mvn clean package

The resulting JAR will automatically include:

  • Base version for Java 17+
  • Optimized version for Java 21+

The JVM will automatically select the appropriate version at runtime based on the Java version being used.

Usage

Minimum Java version: Java 17

Run your application with -javaagent:/path/to/agent.jar=class1,class2,class3

Examples:

# Instrument application classes
java -javaagent:/path/to/allocation-tracker-agent-0.0.1-SNAPSHOT.jar=de.codecentric.MyClass,com.example.OtherClass \
     -jar your-application.jar

# Instrument JDK classes (supported)
java -javaagent:/path/to/allocation-tracker-agent-0.0.1-SNAPSHOT.jar=java.lang.String,java.util.ArrayList \
     -jar your-application.jar

After the agent has printed codecentric allocation agent - Registered Agent MBean. the agent is ready to use. Connect to the JVM with any JMX client and use the de.codecentric.Agent MBean to start tracking allocations. Use stop to stop tracking allocations (this also flushes thread-local buffers). printTop can be used any time to print the allocation hot paths with full stack traces. The parameter will control the amount of output. If the parameter is zero or less, it will default to 100.

The output shows a clean hierarchical tree where parent nodes aggregate counts from all child paths:

Allocation Hot Paths (merged view):
===================================

2000 - java.nio.DirectByteBuffer.<init>
├── 1340 - java.nio.DirectByteBuffer.duplicate
│   ├── 893 - com.sun.crypto.provider.GaloisCounterMode$GCMDecrypt.doFinal
│   │   └── 893 - com.sun.crypto.provider.GaloisCounterMode.engineDoFinal
│   │       └── 893 - javax.crypto.Cipher.doFinal
│   └── 447 - sun.security.ssl.SSLCipher$T13GcmReadCipherGenerator$GcmReadCipher.decrypt
│       └── 447 - sun.security.ssl.SSLEngineInputRecord.decodeInputRecord
├── 456 - java.nio.DirectByteBuffer.slice
│   └── 451 - sun.security.ssl.SSLCipher$T13GcmReadCipherGenerator$GcmReadCipher.decrypt
└── 204 - io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized

The tree structure makes it easy to see allocation hot paths at a glance. The numbers represent total allocations through that call path (including all descendants).

Java 21+ users automatically benefit from additional optimizations including enhanced collection APIs and improved performance characteristics.

Where are the freaking tests?

Glad that you asked. Please take a look at the source files and then come up with a sensible unit test. If you manage to do that feel free to put up a PR.

About

javaagent for finding excessive object allocations

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Java 100.0%