WebObjects 3.5.1 Java Bridge Troubleshooting Guide

This document describes some common problems developers may encounter when using the Java development components of WebObjects 3.5.1, and suggests some ways to work around them.
There are several steps you can follow to resolve or avoid crashes in your Java applications:

Use the best Java Bridge you can

Developers who make extensive use of Java should consider upgrading to WebObjects 4. In addition to bug fixes, the Java Bridge included with WebObjects 4 has enhanced control of asynchronous garbage collection. New features in the WebObjects 4 Java Bridge may allow you to correct problems that may not be fixable with WebObjects 3.5.1.

If upgrading is not an option for your organization, make sure you have the most recent relevant patches installed. The WebObjects 3.5.1 Java Patch 2 contains several Java Bridge bug fixes and should be installed by anyone developing WebObjects applications using Java. For more information, please see the list of current WebObjects patches at:

http://docs.info.apple.com/article.html?artnum=70037

Avoid multiple EOF threads

Some thread safety issues which have been resolved in WebObjects 4 could not be addressed by the WebObjects 3.5.1 patch. You can avoid many JavaBridge-related crashes in a multithreaded EOF application by keeping all of your EOF calls in your main thread.

Increase the Java VM heap size

Java error messages like "StackOverflowError" and "OutOfMemoryError" can be caused by an overflow of the Java Virtual Machine's heap memory. By default, the heap size is 16 MB. Each increase of 16 MB corresponds to an increase of 1,300-1,500 additional active sessions for an average WebObjects application. Apple recommends that you set your Java VM heap size as high as your system resources permit:

//Assume that the JavaVM framework is included with your project
// Add this code to your MyApplication_main.m file


#import <JavaVM/NSJavaVirtualMachine.h>


int main(int argc, const char *argv[])
{
NSAutoreleasePool* myPool=[NSAutoreleasePool new];
       NSMutableDictionary* myArguments=[NSDictionary  
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:32*1024*1024], @"NSJavaMinHeapSize",
               [NSNumber numberWithInt:64*1024*1024], @"NSJavaMaxHeapSize",
nil];
       [NSJavaVirtualMachine setDefaultVirtualMachineArguments:myArguments];
       return WOApplicationMain(@"Application", argc, argv);
}


For a complete list of arguments for NSJavaVirtualMachine, see NSJavaVirtualMachine.h in the JavaVM.framework headers.

Avoid asynchronous garbage collection

Java error messages like "NoSuchMethodError" and "nukeObjCObject" may be caused by the asynchronous garbage collector releasing inappropriate objects. Ordinarily, asynchronous garbage collection will be triggered automatically when the Java VM heap is full. If your heap size is large enough, you can avoid asynchronous garbage collection completely by periodically initiating garbage collection at the end of a request. The following source code explains how to manually initiate the garbage collector every twentieth request, looping through the procedure three times to ensure that all dependent objects are released. You should experiment with different values for the collection frequency and loop count to determine the optimal values for your application.

Add this code to your Application.java file:

public Response handleRequest(Request request) {
      Response response = super.handleRequest(request);
       requestsCount++;
          if (requestsCount % 20 == 0) {
               FullGarbageCollection();
           }


          return response;
       }


Also, modify the FullGarbageCollection code to include a bridge crossing:

  private static void FullGarbageCollection() {

    for (int i=1; i<3; i++) {      // 3 here could be changed
       Runtime runtime = Runtime.getRuntime();
       long isFree = runtime.freeMemory();
       long wasFree;
       do {
           wasFree = isFree;
           runtime.gc();
           isFree = runtime.freeMemory();
       } while ( isFree > wasFree );
       runtime.runFinalization();


       // WARNING: The following line is necessary in order to ensure
       // that the ObjC counterparts of any collected hybrid objects
       // are released (and subsequently dealloc'ed) to clear any weak
       // references. This happens when the main thread crossed the
       // bridge during the wrapped call.
       application();
     }
   }


Retain Java arrays

If a Java method returns a new Java array which is not referenced outside the scope of that method, the Java garbage collector may remove it while its Objective C counterpart is still in use. This will result in an Objective C error messages indicating that a message was sent to a freed object, or a "selector not recognized" error message from the enclosing object when a message is sent to the released object. To avoid this problem, explicitly retain the Java array, vector, or other affected object:

Instead of:

public ImmutableVector myVector() {
    _myVector=<some complex calculation>;
    return _myVector;
}


Do this:

ImmutableVector _myVector;
public ImmutableVector myVector() {
    _myVector=<some complex calculation>;
    return _myVector;
}


Explicitly release autorelease pools

If you create an autorelease pool in your Java code, be sure that you release it manually. For example, if you write the following code:

Incorrect()
{
  AutoreleasePool pool = new AutoreleasePool();
  /* function implementation */
  /* forget about pool, leaving it for the gc to clean up */
}


the objects in the autorelease pool could be released at any point during the execution of the function. Instead, ensure that your autorelease pool is always released manually, even if an exception is encountered:

Correct()
{
  AutoreleasePool pool = new AutoreleasePool();
  try
  {
     /* function implementation */
  }  finally {
     pool.release();
  }
}


In general, you should only create autorelease pools in Java in WebObjects 3.5.1 if you need extremely fine control over the lifetime of objects.

Always call super.finalize()

The finalize() method is called by the Java garbage collector before deallocating your object. If any of your hybrid Java objects implement finalize(), the implementation must call super.finalize() to avoid potentially serious memory leaks. In addition, you should never make a call in a finalize() method that could result in a method invocation across the bridge.

Catch all constructor exceptions

When instantiating a wrapped or hybrid object, checking for a non-null return from your constructor method is not enough. Always use a try/catch statement to ensure that an instantiation error will throw an exception.

Published Date: Feb 20, 2012