Oracle8i Java Developer's Guide Release 3 (8.1.7) Part Number A83728-01 |
Library | Service | Contents | Index

## End-of-Call Migration

Aurora preserves the state of your Java program between calls by migrating all objects reachable from static variables into session space at the end of the call. Session space exists within the client's session to store static variables and objects that exist between calls. Aurora performs this migration operation at the end of every call, without any intervention by you.

This migration operation is a memory and performance consideration; thus, you should be aware of what you designate to exist between calls and keep the static variables and objects to a minimum. If you store objects in static variables needlessly, you impose an unnecessary burden on the memory manager to perform the migration and consume per-session resources. By limiting your static variables to only what is necessary, you help the memory manager and improve your server's performance.

To maximize the number of users who can execute your Java program at the same time, it is important to minimize the footprint of a session. In particular, to achieve maximum scalability, an inactive session should take up as little memory space as possible. A simple technique to minimize footprint is to release large data structures at the end of every call. You can lazily recreate many data structures when you need them again in another call. For this reason, the Aurora JVM has a mechanism for calling a specified Java method when a session is about to become inactive, such as at end-of-call time.

This mechanism is the `EndOfCallRegistry` notification. It enables you to clear static variables at the end of the call and reinitialize the variables using a lazy initialization technique when the next call comes in. You should execute this only if you are concerned about the amount of storage you require the memory manager to store in between calls. It becomes a concern only for more complex stateful server applications you implement in Java.

The decision of whether to null-out data structures at end-of-call and then recreate them for each new call is a typical time and space trade-off. There is some extra time spent in recreating the structure, but you can save significant space by not holding on to the structure between calls. In addition, there is a time consideration because objects--especially large objects--are more expensive to access after they have been migrated to session space. The penalty results from the differences in representation of session, as opposed to call-space based objects.

Examples of data structures that are candidates for this type of optimization include:

- Buffers or caches.

- Static fields, such as Arrays, that once initialized can remain unchanged during the course of the program.

- Any dynamically built data structure that could have a space efficient representation between calls and a more speed efficient representation for the duration of a call. This can be tricky and complicate your code, making it hard to maintain, so you should consider doing this only after demonstrating that the space saved is worth the effort.

### Oracle-Specific Support for End-of-Call Optimization

You can register the static variables that you want cleared at the end of the call when the buffer, field, or data structure is created. Within the Oracle-specified `oracle.aurora.memoryManager`.`EndOfCallRegistry` class, the `registerCallback` method takes in an object that implements a `Callback` object. The `registerCallback` object stores this object until the end of the call. When end-of-call occurs, Aurora invokes the `act` method within all registered `Callback` objects. The `act` method within the `Callback` object is implemented to clear the user-defined buffer, field, or data structure. Once cleared, the `Callback` is removed from the registry.

Note: If the end of the call is also the end of the session, callbacks are not invoked, because the session space will be cleared anyway.

The way you use the `EndOfCallRegistry` depends on whether you are dealing with objects held in static fields or instance fields.

- Static fields--You use `EndOfCallRegistry` to clear state associated with an entire class. In this case, the `Callback` object should be held in a private static field. Any code that requires access to the cached data dropped between calls must invoke a method that lazily creates--or recreates--the cached data. The example below does the following:

  1. Creates a `Callback` object within a static field, `thunk`.

  2. Registers this `Callback` object for end-of-call migration.

  3. Implements the `Callback.act` method to free up all static variables, including the Callback object itself.

  4. Provides a method, `createCachedField`, for lazily recreating the cache.

  When the user creates the cache, the `Callback` object is automatically registered within the `getCachedField` method. At end-of-call, Aurora invokes the registered `Callback.act` method, which frees the static memory.

      import oracle.aurora.memoryManager.Callback;
      import oracle.aurora.memoryManager.EndOfCallRegistry;

      class Example {
        static Object cachedField = null;
        private static Callback thunk = null;

        static void clearCachedField() {
            // clear out both the cached field, and the thunk so they don't
            // take up session space between calls
             cachedField = null;
             thunk = null;
        }

        private static Object getCachedField() {
         if (cachedField == null) {
            // save thunk in static field so it doesn't get reclaimed
            // by garbage collector
            thunk = new Callback () {
                  public void act(Object obj) {
                        Example.clearCachedField();
                  }
            };

            // register thunk to clear cachedField at end-of-call.
            EndOfCallRegistry.registerCallback(thunk);
            // finally, set cached field
            cachedField = createCachedField();
         }
         return cachedField;
        }

        private static Object createCachedField() {
        ....
        }
      }

- Instance fields--Use `EndOfCallRegistry` to clear state in data structures held in instance fields. For example, when a state is associated with each instance of a class, each instance has a field that holds the cached state for the instance and fills in the cached field as necessary. You can access the cached field with a method that ensures the state is cached.

  1. Implements the instance as a `Callback` object.

  2. Implements the `Callback.act` method to free up the instance's fields.

  3. When the user requests a cache, the `Callback` object registers itself for end-of-call migration.

  4. Provides a method, `createCachedField`, for lazily recreating the cache.

  When the user creates the cache, the `Callback` object is automatically registered within the `getCachedField` method. At end-of-call, Aurora invokes the registered `Callback.act` method, which frees the cache.

  This approach ensures that the lifetime of the `Callback` object is identical to the lifetime of the instance, because they are the same object.

      import oracle.aurora.memoryManager.Callback;
      import oracle.aurora.memoryManager.EndOfCallRegistry;

      class Example2 implements Callback {
         private Object cachedField = null;

         public void act(Object obj) {
          // clear cached field
          cachedField = null;
          obj = null;
         }

        // our accessor method
        private static Object getCachedField() {
         if (cachedField == null) {
            // if cachedField is not filled in then we need to
            // register self, and fill it in.
            EndOfCallRegistry.registerCallback(self);
            cachedField = createCachedField();
         }
         return cachedField;
        }

        private Object createCachedField() {
        ....
        }
      }

A weak table holds the registry of end-of-call callbacks. If either the Callback object or value are not reachable (see JLS section 12.6) from the Java program, they will both be dropped from the table. The use of a weak table to hold callbacks also means that registering a callback will not prevent the garbage collector from reclaiming that object. Therefore, you must hold on to the callback yourself if you need it--you cannot rely on the table holding it back.

You can find other ways in which end-of-call notification will be useful to your applications. The following sections give the details for methods within the `EndOfCallRegistry` class and the Callback interface:

##### EndOfCallRegistry.registerCallback method

The `registerCallback` method installs a `Callback` object within a registry. At the end of the call, Aurora invokes the `act` methods of all registered `Callback` objects.

You can register your `Callback` object by itself or with a `value` object. If you need additional information stored within an object to be passed into `act`, you can register this object within the value parameter.

    public static void registerCallback(Callback thunk, Object value);
    public static void registerCallback(Callback thunk);

Parameter | Description
--- | ---
thunk | The Callback object to be invoked at end-of-call migration.
value | If you need additional information stored within an object to be passed into `act`, you can register this object within the `value` parameter. In some cases, the `value` parameter is necessary to hold state the callback needs. However, most users do not need to specify a `value`.

##### EndOfCallRegistry.runCallbacks method

    static void runCallbacks()

The JVM calls this method at end-of-call and calls `act` for every `Callback` object registered using `registerCallback`. You should never call this method in your code. It is called at end-of-call, before object migration and before the last finalization step.

##### Callback Interface

    Interface oracle.aurora.memoryManager.Callback

Any object you want to register using `EndOfCallRegistry.registerCallback` implements the `Callback` interface. This interface can be useful in your application, where you require notification at end-of-call.

##### Callback.act method

    public void act(Object value)

You can implement any activity that you require to occur at the end of the call. Normally, this method will contain procedures for clearing any memory that would be saved to session space.

| Copyright © 1996-2000, Oracle Corporation. All Rights Reserved. |
Library | Service | Contents | Index