Escaping Object References in Java

Stylised symbol of a running man, and an arrow pointing in the direction he must run.

The last few posts have highlighted some of the issues we need to be aware of when working with floating point numbers. In the next few posts we’re going to be looking at some concurrency related topics. This will include ThreadLocal variables and Java 19 virtual threads.

One of these concurrency related topics is escaping object references. This can become an issue in applications with concurrent running threads.

Object Publication and Escaping

We publish an object whenever we make it available to some code outside its current scope. This happens when we pass an object to a method, return it from a non-private method, or store it into a publicly accessible field. We only want to pass objects that are in predictable, consistent states. This only happens after the object’s constructor returns.

An object escapes when it is published before it has been fully constructed. Escapes occur if we publish the object during object construction before it has been fully initialized. It is all too easy to incorrectly let the this reference escape while the constructor is still being run. Other threads could then use that object in an inconsistent (partially initialised) state. This can cause difficult-to-debug threading issues and can compromise thread safety.

Escaping in Constructor Chains

Here’s an example to show the problem of incompletely initialized objects escaping. Even if the object escapes after initialization has completed, declaring a subclass can violate this.

public class SomeClass {
    public SomeClass (AnotherClass obj) {
        obj.foo(this);
    }
}

public class NamedClass extends SomeClass {
    private String name;

    public NamedClass (AnotherClass obj, String name) {
        super(obj);
    }

    public String getName() {
        return name; 
    }
}

If the AnotherClass.foo(...) method calls getName() on the escaped object, it will get null because at that point the constructor chain of NamedClass has not finished.

Escaping with Threads

Starting a thread in a constructor is one of the most common ways of letting the this reference escape. Here’s an example:

public class MyClass implements Runnable {

    public MyClass() {
        Thread thread = new Thread(this); // this explicitly escapes
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("Started...");
    }
}

In this example, we have explicitly passed the this reference to the Thread constructor. The newly started thread might be able to see the enclosing this object before it is fully constructed. With multiple concurrent threads, this could cause hard-to-find bugs.

There’s nothing inherently wrong with creating a Thread inside a constructor. However, it’s not a good idea to start it immediately. If we do, we end up with an escaped this reference, either explicitly (as above) or implicitly (see later).

Instead of starting the thread inside the constructor, we should start it after the constructor has finished. We can create a separate method to do that:

public class MyClass implements Runnable {

    private final Thread thread;

    public MyClass() {
        thread = new Thread(this);
    }

    @Override
    public void run() {
        System.out.println("Started...");
    }

    public void start() {
        thread.start();
    }
}

We still publish the this reference to the thread, but here we start the thread after the constructor ends. The object reference doesn’t escape to the thread before it’s fully constructed.

MyClass safeObject = new MyClass();
safeObject.start();

Escaping with Event Listeners

Using anonymous inner classes to create event listeners is another common way of letting the this reference escape. Here’s an example:

public class MyClass {

    private final int var;  // implicitly initialised to zero

    public MyClass(EventSource source) {
        source.registerListener(
            new EventListener() {
                @Override
                public void onEvent(Event e) {
                    doSomething(e);
                }
            });

        // more initialization
        var = 123;
    }

    // return value could either be 0 or 123
    public int doSomething(Event e) {
        return var;
    }
}

Here we’ve created an anonymous inner EventListener class. The this reference escapes from the constructor, because inner classes have an implicit reference to their enclosing class.

An event could trigger the event listener after its creation, but before the MyClass constructor has finished. The listener then calls the doSomething() method of MyClass. The return value could then either be 0 or 123. This would be a very subtle bug to hunt down.

This example is sneaky because the this reference isn’t visible. It is implicitly escaping to the anonymous EventListener class constructor. However, we’ll have the same problem if the reference is explicitly published too soon, as in the earlier threading example.

Signing Off

Lots more on concurrency to come!

Was this useful? Please share your comments on the blog post, and as always, stay safe and keep learning!

Leave a Comment

Your email address will not be published. Required fields are marked *

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.