: code tutorial (v3)

Lesson 3: The Proxy Object Lifecycle

Introduction

.NET proxy objects represent Java objects that live inside the JVM. Java uses a garbage collector to clean up unused objects, as does .NET for its objects.

That raises the question of how the two languages manage their objects' lifecycles when a .NET object represents a Java object. It's really pretty simple.

.NET Is in the Driver Seat... Mostly

When you use .NET proxy types to control Java objects in an embedded JVM you always start out with .NET code. You create a .NET proxy object and you expect that object to refer to the result of a Java constructor invocation or a method return value or a field value.

When you create such a .NET proxy object, the .NET object becomes the owner of a reference to the Java object. That reference will prevent the underlying Java object from being garbage-collected for as long as the .NET object exists.

When the .NET object is garbage collected, it explicitly releases its hold on the Java object via its object finalizer. You can also accelerate this process by explicitly calling Dispose() on a proxy object. All proxy types implement the IDisposable interface. When you call Dispose(), you tell the proxy object to immediately free its hold on the referenced Java object.

There is a very neat pattern in C# involving the using statement. Let's take a look at a C# snippet in which we have a large array of Java objects that we need to process:

Java.Lang.ObjectArray  arr = myApi.GetResults();
for( int i=0; i<arr.Length; i++ ) {
    using( Java.Lang.Object obj = arr[ i ] ) {
        Process( obj );
    }
}

A using block declares a scope at the end of which the C# compiler inserts an implicit Dispose() call on the "used" object. This has the effect of releasing the Java object much before .NET garbage collection would normally do it. This is totally safe, as long as the object is not cached elsewhere and used outside of the loop. This can significantly ease the performance of a mixed language application when used in tight loops.

The .NET object is effectively "dead" the moment you or the object finalizer has called Dispose() on it, but the same is not true for the Java object which it referenced. Assuming that there were no other references to the same Java object, it merely becomes eligible for garbage collection. It sits there in the Java heap until the Java runtime decides to garbage collect it. So you see that a Java object's lifecycle is tightly coupled to its .NET proxy instance's lifecycle, but it is not identical.

Relying on the .NET garbage collector to drive the lifecycle is usually totally safe. There can be circumstances though where you should really take charge and call Dispose() yourself. Whether that is necessary depends entirely on your application's characteristics and the garbage collector being used. In over twenty years of proxy type usage, there has only been one application that required that the developers explicitly manage their proxy objects' lifecycle... in one place in their code. It is good practice though and we recommend that you do it in tight loops over lots of Java objects. Why? It can make your application significantly faster. The Java garbage collector (there really are many Java garbage collectors; we're generalizing a bit here) is most efficient for short-lived objects. The longer a Java object exists, the harder and more time-consuming it can be to collect. Making Java objects eligible for garbage collection as quickly as possible can therefore have an outsized impact on performance for some applications.

In Summary

The proxy types have been carefully designed to manage the referenced Java objects for you. Constructors, object finalizers, and explicit Dispose() calls work in concert to manage your Java objects' lifecycle.

For most practical purposes you can treat the life cycle of a Java object as the union of the lifecycles of all .NET proxy instances that are holding references to the object. In other words: a Java object usually "dies" soon after the last .NET object that held a reference to it has been destroyed.