Home for HMNL Enterprise Computing

The Use of recycle()

Ian Tree  20 March 2006 09:20:21

The Use of recycle()


The use of the recycle() method on Domino Java objects can be one of the most perplexing areas of programming for Notes/Domino developers. The reason for this is that the underlying principles of why this is necessary and the methods and techniques needed to implement it correctly will only be familiar to those of us that have needed to develop serious application components using the C API. However the good news is that you don't have to become a C API developer to be able to develop industrial strength, efficient Java code using the Domino Object set. Read on to find out what it's all about, why it's there and how to use it effectively.
  • The Notes Programming Object Model for Java and LotusScript
  • Why is it Important to Use recycle()
  • When and What to recycle()
  • Implicit Recycling
  • Common Design Patterns
    • Iterating a View
    • Container Classes
  • Best Practices

The Notes Programming Object Model for Java and LotusScript


All of the Programming Interfaces used in Notes and Domino are based an underlying set of Object Services called Notes Object Service (NOS), this underlying layer is implemented in C++ and then exposed to the different programming environments through a variety of Application Programming Interfaces. All of the Objects that are created in the OO programming environments such as Java and LotusScript are implemented as "shadow" objects, i.e. each object in the native program space is a placeholder for the real object in the NOS C++ space. Objects that are implemented in the NOS implement containment, one object can "contain" another, remember this because it will come back later to cause some complications. The following diagram illustrates the object relationships in the different domains.

Object Model




The Java Domino Objects all extend the Base class, the Base class contains methods that, through the Java Native Interface (JNI) can access the "real" objects in the NOS layer. When you create a new Domino object in Java, a part of the process is to create the real backend object in the NOS layer. The recycle() method is the interface that is used in the Java layer to tell the "real" object in the NOS layer to destroy itself.

Why is it Important to Use recycle()

On the Java side the "garbage collection" mechanism will ensure that the memory accumulated from the creation of objects are released when the object instance goes out of reference scope, or is explicitly de-scoped by removing the last reference to it. However there is no mechanism to synchronize the Java "garbage collection" process with the "real" objects in the NOS layer. Therefore it is clear that as a Java Domino program was run there would be a continual growth in the memory used by the NOS layer until finally it would run out of memory. There are some more subtle reasons for it to be important to synchronize the Java and NOS sides of the object model, to do with the non-deterministic state of any transaction in the Java layer, but memory remains by far the most important reason for the need for synchronization.

When and What to recycle()

A Domino object instance should be recycled immediately before it goes out of reference scope in Java. This statement is easily understood by the more experienced Java programmer but probably needs some further qualification for the less experienced. Consider the following snippet of code.

{
   Database dbX = sessCurrent.getDatabase(......);    // #1
   .
   .
   .
   dbX = sessCurrent.getDatabase(........);       // #2
   .
   .
}               // #3


In the code snippet the statement marked #1 will create a new Database object instance (and create a reference to it in the variable dbX). The statement marked #2 will create a new Database object instance, it will also create a reference to it in the variable dbX, as a side effect the last reference to the Database object instance created in #1 is removed. The removal of the last reference to the Database object instance created in #1 will make that instance eligible for "garbage collection" and at some point the memory occupied by the instance will be released. The statement marked #3 will remove the variable dbX from scope and as a side effect will remove the last reference to the Database object instance created at #2, making it eligible for "garbage collection". The Database object instance created in #1 must be recycled before #2 and the one created in #2 must be recycled before #3, this would make our code snippet look like the one below.
{
   Database dbX = sessCurrent.getDatabase(......);    // #1
   .
   .
   dbX.recycle();
   dbX = sessCurrent.getDatabase(........);       // #2
   .
   dbX.recycle();
}               // #3

The above model applies to ALL Domino Objects. Databases, Views and Documents are the most obvious objects that need to be recycled but do not forget about some of the lower level objects that are needed in different coding scenarios Item, Name, DateTime to name but three, these again must be recycled. Although the last statement may lead you to conclude that a Domino Java program will consist of mostly recycle() statements, the situation is not quite so bad, because, you can rely on implicit recycling.

Implicit Recycling


Remember that earlier I mentioned the use of containment in the NOS object model. This is implemented by having all of the Item objects that are derived from a particular Document object "contained" by that Document object. The NOS implements the containment rule that all "contained" objects are destroyed when the parent container object is destroyed. What this means in practice is that in a block of Java code that creates and recycles a document you do not need to explicitly recycle any Item objects that you create in that block as they will all be implicitly recycled when the Document object is recycled. If you rely on Implicit Recycling then it is important that carefully manage the scope of variables that reference Domino objects, mistakes can be perplexing and take a long time to track down. Look at the code snippet below.

private Item itmTemp = null;         //  #1
.
.
.
public void doProcess1() {
   .
   Document docTemp = ......
   itmTemp = docTemp.getFirstItem("X");             //  #2
   docTemp.recycle();                                //  #3
}
public void doProcess2() {    Document docX = ...........                        //  #4
   if (docX != null)                                //  #5
   {
       .
       itmTemp = docX.getFirstItem("Y");        //  #6
       .
   }
   .
   .
   String strYValue = itmTemp.getValueString();   // #7

}


In the above code snippet the doProcess1() method is called before the doProcess2() method. If during the execution of the statement marked #4 no document was returned then the statement marked #6 would not be executed because of the conditional (#5). Statement #7 would throw a NotesException with the reason "Object has been removed or recycled".
This error could prove to be a nightmare to debug, particularly if the code snippet was part of a large class. Attention would focus on the doProcess2() method and the assumption would be that docX must have contained a valid document at statement #5 because statement #6 must have been executed otherwise there would have been a null pointer exception at statement #7. This assumption would lead in completely the wrong direction, the problem is caused by the combination of relying on Implicit Recycling and bad variable scope management in the implementation of the doProcess1() method (I will not mention the lack of defensive style in the implementation of the doProcess2() method...... that is a topic for a separate article).
During the execution of doProcess1() the recycle() at statement #3 has caused the Document to be destroyed and the contained Item is implicitly recycled (i.e. destroyed in the NOS layer). However because of the scope of the itmTemp variable the Java object instance for the Item created in #2 remains referenced. Without the itmTemp variable being updated in statement #6 the statement #7 will reference the Java object that was created at statement #2 but the NOS layer object for that Item no longer exists. The result is the NotesException saying, quite correctly, "Object has been removed or recycled". See the "Best Practices" section at the end of this article for some techniques to avoid this situation.

Common Design Patterns


This section contains some common design patterns for the use of recycle() in Domino Java code.

Iterating a View


It can be confusing to see how the recycle() method should be applied to Documents returned through the View.getNextDocument() method as the Document at the current position must be passed into the method as a parameter. The code snippet below illustrates the correct design pattern for this construct.

View vLotsOfDocuments = dbX.getView("LotsOfDocuments");
Document docCurrent = vLotsOfDocuments.getFirstDocument();
while (docCurrent != null)
{
   .
   ..... process the current document
   .
   Document docPosition = docCurrent;
   docCurrent = vLotsOfDocuments.getNextDocument(docCurrent);
   docPosition.recycle();
}
vLotsOfDocuments.recycle();


Container Classes


Objects that have to contain Domino objects as members should implement a recycle() method of their own to allow them to be used in higher level objects without risk. The code snippet below illustrates a trivial implementation of such a class.

public class SampleContainer {

private Name nnX = null;                // A Name Object
private DateTime dtY = null;                // A DateTime Object

/*   Recycle Method         */
public void recycle() throws NotesException {
   if (nnX != null)
   {
       nnX.recycle();
       nnX = null;
   }
   if (dtY != null)
   {
       dtY.recycle();
       dtY = null;
   }
}
/*   Getters & Setters       */
public Name getX() {
   return nnX;
}
public DateTime getY() {
   return dtY;
}
public void setX(Name nnNewValue) {
   nnX = nnNewValue;
}
public void setY(DateTime dtNewValue) {
   dtY = dtNewValue;
}
}


Best Practices

  1. Explicitly set variables to null when the object instance that they reference has been recycled.
  2. Keep the scope of variables that reference Domino objects as local as possible.
  3. Recycle Domino objects as early as possible.

Comments