Friday, December 16, 2016

Day 16, Java Holiday Calendar 2016, Hacking Java Classes

16. Hacking the Existing Java Classes



Today's tips is about hacking the existing Java classes. But before we start, I would like to issue a disclaimer: I am only showing this to illustrate certain possibilities. This post contains code that  is against best-practice and also a number of license terms for commercial JVMs and I would discourage anyone from using this for purposes other than just experimenting in dark cellars at late nights.

The Task

There is a lot of magic going around with auto-boxing and instance caching with wrapper classes like java.lang.Integer. Suppose we could hack the Integer class so it will keep track of how many of its instances are created and how many instances are garbage collected and that we could add a way of retrieving this statistics in some form? How do we go about?

Open Sesame

The JVM has a command line parameter named "-Xbootclasspath/p:" that can be used to load Java classes before the standard java classes are loaded. This way, we can inject our own versions of Java classes before the real ones are loading.

This is useful also for other classes that are not a part of the standard Java library. If there is a bug in a third party product, we can correct the bug and just load the correct version before the app loads. It also enables us to instrument classes so they, for example, can collect usage statistics or introduce or prevent certain things from happening.

Hacking the Integer class

Create a custom class named Integer and place that in a package named java.lang. Retrieve the standard source code of Integer form the JDK and copy that into the custom java.lang.Integer class.

Add the following at the beginning of the copied Integer class:

    public static final AtomicLong INSTANCE_CREATE_COUNTER = new AtomicLong();
    public static final AtomicLong INSTANCE_FINALIZE_COUNTER = new AtomicLong();

Modify the constructor like this:

    public Integer(int value) {
        this.value = value;
        INSTANCE_CREATE_COUNTER.incrementAndGet();
    }

Finally, add the following method:

    @Override
    protected void finalize() throws Throwable {
        INSTANCE_FINALIZE_COUNTER.incrementAndGet();
    }

That's it! We have hacked the Integer class. The modified class can now be used like this:

package org.darkside.hack;

public class Main {

    public static void main(String[] args) {

        printIntegerInstanceInfo();
        Integer i = 456;
        printIntegerInstanceInfo();
        System.gc();
        sleep(1000);
        printIntegerInstanceInfo();

    }

    public static void printIntegerInstanceInfo() {
        long create = Integer.INSTANCE_CREATE_COUNTER.get();
        long finalized = Integer.INSTANCE_FINALIZE_COUNTER.get();
        System.out.format(
            "The JVM has created %d instances and finalized %d instances of Integer."
                + " Thus, %d instances are on the heap.%n",
            create,
            finalized,
            create - finalized
        );
    }

    public static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException ie) {

        }
    }

}

Running the Code

The code can be run like this:

java -Xbootclasspath/p:hack_java_class-1.0.0-SNAPSHOT.jar org.darkside.hack.Main

We have to change the name of the .jar file depending how we named our artifact. Make sure that the modified Integer class and the Main class resides in the same jar. We can also have the modified classes in a separate jar but then we need to add the other parts using the normal class path commands.

Wrap up

The notion here is "Don't Try This at Home". That said, we can learn a lot by injecting custom classes into the JVM.

There are many better ways of keeping track of how many instances that are created and destroyed by the JVM. The task in this post is just for the sake of reasoning.

Be careful out there!





No comments:

Post a Comment

Note: Only a member of this blog may post a comment.