-
Notifications
You must be signed in to change notification settings - Fork 90
Home
The goal of the allocation instrumenter is to track allocations as they occur in a Java program.
The latest release is available from Maven Central as:
<dependency>
<groupId>com.google.code.java-allocation-instrumenter</groupId>
<artifactId>java-allocation-instrumenter</artifactId>
<version>3.0</version>
</dependency>
You can also build the source yourself by checking it out and running mvn package
. This will create a file called java-allocation-instrumenter-${version}.jar
in the target/
subdirectory. Alternatively, you can download the jar here.
Assume that you have a program that creates 10 strings, and you want to instrument that creation:
public class Test {
public static void main(String [] args) throws Exception {
for (int i = 0 ; i < 10; i++) {
new String("foo");
}
}
}
To do this, you create an instance of the interface Sampler
:
import com.google.monitoring.runtime.instrumentation.AllocationRecorder;
import com.google.monitoring.runtime.instrumentation.Sampler;
public class Test {
public static void main(String [] args) throws Exception {
AllocationRecorder.addSampler(new Sampler() {
public void sampleAllocation(int count, String desc, Object newObj, long size) {
System.out.println("I just allocated the object " + newObj
+ " of type " + desc + " whose size is " + size);
if (count != -1) { System.out.println("It's an array of size " + count); }
}
});
for (int i = 0 ; i < 10; i++) {
new String("foo");
}
}
}
You can then compile and run the program:
% javac -classpath path/to/allocation.jar Test.java
% java -javaagent:path/to/allocation.jar Test
The output will look something like this:
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
I just allocated the object foo of type java/lang/String whose size is 24
...
And so on.
Allocation instrumentation allows the user to instrument all of the allocations that a program executes. The allocation instrumenter can also instrument the construction of particular classes. Assume that you have a program that creates 10 instances of type Test
, and you want to track those creations:
import java.lang.instrument.UnmodifiableClassException;
import com.google.monitoring.runtime.instrumentation.ConstructorInstrumenter;
import com.google.monitoring.runtime.instrumentation.ConstructorCallback;
public class Test {
static int count = 0;
int x;
Test() {
x = count;
}
public static void main(String[] args) {
try {
ConstructorInstrumenter.instrumentClass(
Test.class, new ConstructorCallback<Test>() {
@Override public void sample(Test t) {
System.out.println(
"Constructing an element of type Test with x = " + t.x);
count++;
}
});
} catch (UnmodifiableClassException e) {
System.out.println("Class cannot be modified");
}
for (int i = 0; i < 10; i++) {
new Test();
}
System.out.println("Constructed " + count + " instances of Test");
}
}
You can then compile and run the program:
% javac -classpath path/to/allocation.jar Test.java
% java -javaagent:path/to/allocation.jar Test
The output will look something like this:
Constructing an element of type Test with x=0
Constructing an element of type Test with x=1
Constructing an element of type Test with x=2
Constructing an element of type Test with x=3
Constructing an element of type Test with x=4
Constructing an element of type Test with x=5
Constructing an element of type Test with x=6
Constructing an element of type Test with x=7
Constructing an element of type Test with x=8
Constructing an element of type Test with x=9
Constructed 10 instances of Test
This approach also adds hooks to instrument all of your allocations. This may slow your code down. If you want to instrument constructors of named classes, but not allocations, you can change your command line flag thus:
% javac -classpath path/to/allocation.jar Test.java
% java -javaagent:path/to/allocation.jar=manualOnly Test
manualOnly
implies that we will only use the constructor instrumenter (i.e., we will instrument each class manually).
Note that the constructor instrumenter will instrument every constructor in a given class. In classes with chained constructors, the instrumentation will be run multiple times: once for each constructor invoked. To guard against this, you may wish to examine the stack using Thread.getStackTrace
or Java 9's StackWalker
API; if you are multiple levels deep in the <init>
methods of a given class, you may be in a constructor chain.
Of specific concern is that, in JDK 11, the Thread constructors now use chaining. Those attempting to instrument Thread construction need to guard against the fact that their constructor initializers may be executed multiple times.