: code tutorial (v3)

Lesson 6: Dealing With null

Introduction

In both .NET and in Java, the keyword null is used to indicate that a variable or argument does not refer to a valid object.

The proxy types are designed in such a way that any place that accepts a Java null value accepts a .NET null value.

This design allows you to write code like:

MyProxyType pt = new MyProxyType();

if( pt.field == null ) {
    Console.Error.WriteLine( "Field value is null!" );
}

pt.field = null;

You can of course also use null as a method argument if the underlying Java method accepts null as a value.

When a Proxy Becomes null

In contrast to the unmanaged C++ bindings, you should never have to worry about a .NET proxy instance internally having a Java null value. If a Java API returns a null value, the corresponding .NET resultd will be null, not a proxy object that references null.

That being said, it is possible to get into situations where a previously valid proxy instance has become invalid. One such situation can arise if the using statement is employed incorrectly.

For background, all proxy objects implement the .NET IDisposable interface and its Dispose() method. The Dispose() method is usually called by the proxy instance's object finalizer during garbage collection to release the Java reference being held by it.

You can explicitly call Dispose() to release the held Java reference or you can use the using statement to have the .NET runtime call Dispose() at a defined point. The following example illustrates the proper use of using.

using( MyProxyType pt = new MyProxyType() ) {
    pt.DoSomething();
}

In this snippet, the .NET runtime will automatically invoke Dispose() on pt at the end of the using block. Dispose() will use JNI to free the held Java reference and set the proxy instance's internal reference to null to prevent a double free. This is totally unproblematic because you have no reference to pt anymore once the block is done.

But consider this slightly different, and totally improper, snippet:

MyProxyType pt = new MyProxyType();

...

using( pt ) {
    pt.DoSomething();
}

...

if( pt != null )
   pt.DoSomething();

The problem with this snippet is that it is essentially equivalent to:

MyProxyType pt = new MyProxyType();

...

try {
    pt.DoSomething();
}
finally {
    pt.Dispose();
}

...

if( pt != null )
   pt.DoSomething();

As you can see, the Java reference maintained internally by pt will be null after the call to Dispose(), but pt itself will still hold a valid object reference. So it will pass the null test at the end and try to invoke the proxy method DoSomething(). Unfortunately, at that point, the Java reference on which it tries to invoke the method will be null and the result will be a Java NullPointerException.

It should be noted that this problem is entirely due to improper usage of the using statement, or better, on improper usage of an object after it has been disposed of by a using statement.

Don't let this discussion drive you away from liberally employing using statements in your programs. Releasing Java references as early as possible can have a huge positive impact on your application's performance, particularly in tight loops. Consider this snippet:

for( int i=0; i<10000000; i++ ) {
    using( MyProxyType pt = MyFactory.GetInstance( i ) ) {
        pt.DoSomething();
    }
}

We are essentially creating ten million Java instances, just to call one method on each one, and then we can forget about it again. Without using or a manual call to Dispose(), these Java references will sit around for a long time, possibly gradating into long-lived objects that are more expensive to garbage collect. With using, every instance immediately becomes eligible for collection right after it has been used. Admittedly, this is a constructed example, but the performance impact really can be dramatic in tight loops (database result sets, etc.).