C++-like resource management in Java

One of the reasons why many programmers prefer C++, is that it features “deterministic resource deallocation”. Reading this phrase, I often ask myself: “Do other languages feature non-deterministic resource deallocation?” In my opinion, non-determinism is not a feature, but rather a shallow understanding of the functionality of C++ destructors and the purposes (and consequently limitations) of garbage collection.

What is a destructor in C++? A simple answer might sound like this: “A destructor is a function, which is invoked right before freeing an object’s memory.” Consequently, one might ask: “How can I execute code in Java before freeing an object?” And we arrived at finalizers, which have no use case in a purely managed environment.

The reason for this confusion is, in my opinion, thinking on a wrong level of abstraction. Garbage collected languages typically provide no way to directly manage memory – memory management is below the abstraction level of the language. Why would you bind your program’s behavior to implementation details of the language?

In the following, this post explores the possibilities of C++-like resource management in languages featuring automatic memory management. First, we take a closer look at the functionality of C++ destructors, and consider alternatives. Finally, I propose an implementation of an atypical feature of C++ destructors in Java 8, and discuss its limitations.

What do C++ destructors do? What alternatives do other languages have?

  • Guarantee that a code is called when execution leaves scope, including the case of stack unwinding when exceptions are thrown.
    Other languages (Java, C#, Python etc.), feature try-finally blocks to achieve the same effect.
  • Strongly couple resource acquisition and release, resource release happens at the end of block.
    Other languages provide a similar features with different names, Java 7 has try-with-resources, C# has the using statement with the IDisposable interface, and Python has the with statement.
  • Automatically generated to call destructors of members and base classes.
    Atypical feature, most languages lack a direct alternative.

What is exactly this “atypical feature”?

If we define a C++ class widget as …

class widget {
    gadget g;
};

then the compiler automatically generates the “following” destructor:

void widget::~widget() {
    g.~gadget();
}

(When we define a destructor, then the compiler-generated destructor code is appended to the user-defined code.)

In Java, because of automatic memory management, this is unnecessary in most cases. However, if Widget holds a non-memory resource indirectly through Gadget, then we usually need to write the following manually:

public class Widget {
    private Gadget gadget;

    public void dispose() {
        gadget.dispose();
    }
}

Is there a way to avoid having to tediously write such dispose methods? Many languages let us achieve this via reflection. In the rest of this post, we will stick to Java, although the ideas are often applicable to other languages as well.

Implementation

All code is available on GitHub. At the heart of my implementation lies the Destructible marker interface:

public interface Destructible extends AutoCloseable {
    // The dispose() method, if defined, is called via reflection.
    // void dispose() throws Exception;

    default void close() throws Exception {
        recursiveDispose(this);
    }

    static void recursiveDispose(Object object) throws Exception {
        // see on GitHub
    }
}

A dispose() method, if defined, is called at resource release via reflection. A default implementation of the close() method is provided, so that a Destructible type is also AutoCloseable, i.e. it can be used in a try-with-resources block.

recursiveDispose, the implementation of close(), does the following:

  1. If object is null, or neither Destructible nor AutoCloseable: nothing happens.
  2. If object is AutoCloseable, but not Destructible: close() is called.
  3. If object is Destructible: recursiveDispose is called recursively for all fields not annotated with @NoOwnership.

Usage

  • Make your class implements Destructible.
  • Use @NoOwnership annotation on fields, which are AutoCloseable or Destructible, but are not meant to be automatically released. (Similar to non-owning pointers in C++.)
  • Always acquire and release the root object in try-with-resources.

Here’s a little example which demonstrates, that dispose() is recursively called for all members:

public class ExampleOne {
	public static class A implements Destructible {
		private B first, second;

		// ...
		
		public void dispose() {
			System.out.println("A object disposed.");
		}
	}
	
	public static class B implements Destructible {
		private int id;
		
		// ...
		
		void dispose() {
			System.out.println("B object " + id + " disposed.");
		}
	}

	public static void main(String[] args) throws Exception {
		try (A a = new A(new B(), new B())) {
			// do nothing
		}
	}
}

The program prints:

A object disposed.
B object 1 disposed.
B object 2 disposed.

We see that order is the same as in C++: first the object’s own “destructor” is called, then the “destructors” of its fields.

Ownership cases: shared ownership?

Modern C++ conventions define three basic types of ownership: raw (non-owning) pointers, unique pointers and shared pointers. In the above implementation, our Java references are assumed to mean unique ownership (otherwise a resource may be doubly released), unless they are annotated with @NoOwnership, which implies no ownership. How can we handle cases when a resource is shared by several objects?

The Shared class provides a counterpart to C++’s shared_ptr, by wrapping a reference and a reference count, increasing the reference count at each clone(), decreasing it at each dispose(), and finally calling close() when it reaches zero.

A tiny example demonstrating “shared references” in action:

public class ExampleTwo {
	public static class Resource implements Destructible {
		public Resource() {
			System.out.println("Resource acquired: " + this + ".");
		}

		public void access() {
			System.out.println("Resource accessed: " + this + ".");
		}
		
		public void dispose() {
			System.out.println("Resource released: " + this + ".");
		}
	}

	public static class MyObject implements Destructible {
		private Shared<Resource> resourceRef;
		
		public MyObject(Shared<Resource> resourceRef) {
			this.resourceRef = resourceRef.clone();
		}
		
		public void doSomething() {
			resourceRef.getObject().access();
		}
	}
	
	public static void main(String[] args) throws Exception {
		try (Shared<Resource> resource =
					 new Shared<Resource>(new Resource());
			 MyObject one = new MyObject(resource);
			 MyObject two = new MyObject(resource)) {
			one.doSomething();
			two.doSomething();
		}
	}
}

The program’s output:

Resource acquired: destructor.ExampleTwo$Resource@1db9742.
Resource accessed: destructor.ExampleTwo$Resource@1db9742.
Resource accessed: destructor.ExampleTwo$Resource@1db9742.
Resource released: destructor.ExampleTwo$Resource@1db9742.

As we see, the “resource” is correctly released just once, and that the same “resource” is used through both MyObjects. However, we needed special care not to forget to clone() in MyObject‘s constructor.

Discussion

In typical C++ programs, memory is by far the most often used resource, and has the most complex interactions. (Unlike file handles, network sockets etc. memory objects can refer to each other and to other type of resources.) In a language featuring automatic memory management, like Java, memory need not be regarded as a resource, which significantly reduces the pressure to have advanced resource management techniques.

We discussed the substitutes for the features of C++ destructors, and proposed a proof of concept implementation of a feature with no direct alternative, although the presented solution can be criticised in several ways:

  • The whole idea of imitating C++’s resource management in another language smells like the Golden Hammer Antipattern. I would really like to see a real-world example, where solely using try-finally and try-with-resources (and garbage collection for memory) would be too painful.
  • Using run-time reflection for calling dispose() methods is definitely slower than generating those methods at compile-time, as a C++ would do. Manually delegating dispose() calls – as first shown in this post – should have comparable performance to C++’s approach, but of course, that is manual work for the programmer.
  • Shared references are very fragile:
    • C++ lets you overload the assignment operator, here you must not forget to clone() when creating an independent reference.
    • Minor inconvenience, but you also must use getObject() when you need the underlying resource.
Advertisements

4 thoughts on “C++-like resource management in Java

  1. Interesting idea!

    I’d just take issue with classifying memory as the resource with the most complex interactions (I also think it’s unlikely it’s by far the most used resource; in general, it’s hard to say anything about the most used feature/construct/application in C++ in any case, it’s a _highly_ general-purpose language).

    Now, as for the complexity, personally I think that concurrency resources are more complex (threads, locks, synchronization constructs, etc.) — not only can they refer to themselves (with complex dependency graphs of do-computation-A after-computation-B-done given-that-computation-C-completed-with-flag-D, etc.) but also to memory resources (some of them shared, some of them even mutable, etc.).

    That being said, perhaps memory is still a strong runner-up (especially if we count IPC (interprocess communication) “files” as “memory”, too).

  2. What alternatives do other languages have? Python has __del__ destructor method, Lua has __gc destructor methamethod and D language has destructors. Python even have a convention for handling circular references.

    It is cute how you market lack of memory management in C++ as do it yourself “deterministic resource deallocation” 😀

    • I might have been unclear, but it was not my intention to promote C++. The reason for using quotes around “deterministic resource deallocation” is that I have read the exact same expression from several people, but I would not use it myself like that, since I feel that it falsely implies that other languages might have “non-deterministic” resource deallocation. Which is, in my opinion, a misunderstanding, and the point of this post is to try to clarify that.

      The __del__ method in Python and the __gc metamethod in Lua are not like C++ destructors. They are actually finalizers, which should not be interpreted as an alternative to C++ destructors. Although, in case of CPython, it is quite easy to confuse, since its primary garbage collection method is reference counting, which makes the __del__ method behave in most cases as a C++ programmer would expect from a destructor.

      D indeed has C++-like destructors, which is not surprising considering the fact, that D is actually a re-engineered C++, without the costs and benefits of backward compatibility, plus a lot of other features.

    • It’s not do-it-yourself. C++ handles it well with its unique and shared pointers. You don’t have to reinvent the wheel. On the other hand Java lacks good resource management other than memory, that’s why the whole idea of making somewhat-C++-like management in this article.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s