Spot the WTF (Java knowledge required)



  • class UnrelatedException extends Exception {
    <font color="DARKGREEN">// need this for better error reporting</font>
    private final Thread creator = Thread.currentThread();

    public UnrelatedException(String message) {
    super(message);
    }

    public UnrelatedException(String message, Throwable cause) {
    super(message, cause);
    }

    public UnrelatedException(Throwable cause) {
    super(cause);
    }

    <font color="DARKGREEN">// a static instance for some weird internal use</font>
    private UnrelatedException() {/* nothing */}
    private static final UnrelatedException instance = new UnrelatedException();

    <font color="DARKGREEN">// other class stuff</font>
    <font color="DARKGREEN">// ...</font>

    <font color="DARKGREEN">// method, just happened to be declared here</font>
    public static void innocentlyLookingUtilityMethod() {
    <font color="DARKGREEN">// code having little to do with UnrelatedException class</font>
    }
    }

    final class AllocatingALotTemporaryThread extends Thread {
    private final byte[] array = new byte[Runtime.getRuntime().freeMemory() / 2];

    @Override public void run() {
    <font color="DARKGREEN">// ...</font>
    UnrelatedException.innocentlyLookingUtilityMethod();
    <font color="DARKGREEN">// ...</font>
    }
    }

    P.S. If at first you don't succeed, it can be too late to edit already, but not too late to delete...



  • Ummm, the thread invokes the function.  The function name has the WTF of using an adverb instead of an adjective in the name.

    Anyway, this causes the class to load.  That causes the static class members to be created, including the instance named "instance".  That causes a thread reference to be stored in "instance".  Voila, the thread cannot be GC'd (at least until the class is reclaimed).
     



  • Yep, and classes are never reclaimed (de-facto there) - my
    congratulations for cracking this so fast. I really enjoyed tracking
    this down. To make things worse, method was only called in some rare
    situations, and, of course, sometimes class was loaded by different
    thread...



  • @UpNDown said:

    Ummm, the thread invokes the function.  The function name has the WTF of using an adverb instead of an adjective in the name.

    Anyway, this causes the class to load.  That causes the static class members to be created, including the instance named "instance".  That causes a thread reference to be stored in "instance".  Voila, the thread cannot be GC'd (at least until the class is reclaimed).
     

    Clever, I didn't think of that.

    Then again, I haven't been thinking clearly this morning; skipped breakfast :(



  • @maratcolumn1 said:

    Yep, and classes are never reclaimed (de-facto there) - my congratulations for cracking this so fast. I really enjoyed tracking this down. To make things worse, method was only called in some rare situations, and, of course, sometimes class was loaded by different thread...

    The anonymization was done very well for this post



  • Shame they don't teach this stuff at uni.


     So you're stuck with a reference until the class is instantiated again. What is the problem when the thread is completed? Does a thread still claim resources despite it being completed when it is still referenced? Or is the issue that you're referencing a thread that has finished, unecessarily?
     



  • Shame they don't teach this stuff at uni.

    It is not their task - they should teach how you to find this out yourself.

    So you're stuck with a reference until the class is instantiated again. What is the problem when the thread is completed? Does a thread still claim resources despite it being completed when it is still referenced? Or is the issue that you're referencing a thread that has finished, unecessarily?

    Thread has no special meaning after completing, it is just an object. So from GC's point of view array stays accessible as ((AllocatingALotTemporaryThread)UnrelatedException.instance.creator).array.

    The unexpected thing here is late class loading performed by the thread that accessed the class first. Therefore Thread.currentThread() accessed from static initializer may produce just anything. In C++ for instance attempting to get current thread (whatever it may mean) from static initializer would return you the main thread without any surprises.

    As for class unloading read 12.7 Unloading of Classes and Interfaces in the Java Language Specification, it is hard to explain better. The real WTF is they were actually unloaded in the prehistoric times.


Log in to reply