Home

Dietmar Höhmann

Stop recycling yourself! Employ the Recycler!

But why should I do that?

First: Convenience
Import the Recycler, add two (or at least one) statements to your code and forget about recycling.
The recycler takes care for you. Invisible under the hoods.
An example:

import biz.it_con.domino.Recycler;
import lotus.domino.*;
public class JavaAgent extends AgentBase {
    public void NotesMain() {
    try {
        Session session = Recycler.monitor(getSession());
        AgentContext agentContext = session.getAgentContext();
        System.out.println(session.getUserName());
        System.out.println(agentContext.getCurrentDatabase().getTitle());
        Database db=agentContext.getCurrentDatabase();
        for (int j=0; j<30000; j++) {
            Document doc3=db.createDocument();
            doc3.replaceItemValue("Test", "Test");
            //doc3.recycle(); //No recycling needed!
        }
        Recycler.cleanup();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

If we'd do the same without Recycler, we would have to recycle every created document object.
Otherwise we would run out of handles. Of course this is a very simple example, but who codes in
Java knows how difficult recycling can be.
Notice that you just have to call Recycler.monitor? That's in fact all you need. Recycler.cleanup() is
optional (at least in agents). If called, it does a garbage collection and recycles every unreferenced
Domino object (in normal operation recycling is done in batches from time to time). If you use
cleanup after the last reference to any Domino object all objects are recycled, including the initially
monitored session. This is because in Java an object goes out of scope and becomes ready for
garbage collection immediately after the last statement that reads it.

Second: Clean code
With manual recycle it is absolutely impossible to write clean, secure and stable code! At least if
projects reach a certain size.
Imagine this situation: You are required to code a library that will be used in various projects.
Should the library functions recycle the Domino objects they use, or not? Of course they must
recycle, because you don't know how many object handles the final application will use and your
ones might be too much, if not recycled. On the other hand, libraries must never ever recycle any
Domino object. As you probably know, every Domino object has no more than one Java object. And
if a library function uses an object (i.e. a view) that is also used by the caller (or any other object),
recycling this object will bring the caller in trouble! And the same is in general true for documents.
This makes recycling an insecure operation if you don't know exactly the current state of the
(entire!) application.
The Recycler removes this problems because it keeps track of all created Domino objects and
recycles those objects that are no longer used – no more and no less. And – of course – it will keep a
parent object until all of its child objects went out of business. You know: Recycling a view recycles
all documents drawn from that view, too. With the Recycler you are released from keeping parent
objects that you no longer need just for later recycling.

How do I use it?

Call Recycler.monitor, pass it a Domino object and get back a proxy for the object passed in. The
proxy will watch the initial object you passed in and all objects drawn from it. In general, that's all
you have to do. You may use any Domino object as parameter to monitor(), a Session, a Database or
even a DateTime. But normally you'd just let the Recycler monitor the session and you're done.
You may call Recycler.cleanup at the end of your code. Be sure to not reference any Domino
objects after cleanup.
And you could call Recycler.doHousekeeping at any time to free up resources, i.e. if you use idle
time for housekeeping. This function also is the one to call if you need a certain object to get
recycled. I.e. if you want to clear the cache and re-read a document from the database, set all
handles to the object to null, call doHousekeeping, then re-get the document.

What Versions of Notes/Domino are supported?

Recycler uses features of Java 1.5. Therefore it requires Notes/Domino 8 or higher. Back porting to
R6/R7 would be possible, but as Versions 6 and 7 are fading out, it's probably not worth the effort.
Note: Projects using Recycler must be set to a source compatibility of at least 1.5. This can be set
on a project basis (i.e. per agent or script library) under “Project/Properties/Java compiler”.

If I employ the Recycler in existing code, am I required to remove all calls to recycle?

No. If you call recycle on an object that is monitored by the Recycler the call is silently ignored.

Is the Recycler thread safe?

Yes, if you set it to. When you want to use the recycler in a multi-threaded environment call
Recyler.setSynchronised(true) to switch Recycler to synchronised mode.

Does the Recycler impact performance?

As you might guess, the Recycler has a performance hit. Every call on a Domino object passes
through a proxy and that takes time. No extensive performance testing has been done by now. A
basic test on my development notebook added about 30 microseconds overhead to each call (i.e.
one million calls on Domino objects would add about 30 seconds). I'm sure this is acceptable in
most situations.

And how is it licensed?

The Recycler is licensed under the LGPL version 3. See License.txt or
http://www.gnu.org/licenses/ for details. So you are free to use it in commercial and non
commercial projects. If you make money with the Recycler it would of course be very appreciated
if you pass me on a share but you are not required to.

May I take a look under the hoods?

Let me explain some technical details of the Recycler. When you pass in an object for monitoring,
the Recycler stores it in an internal list and returns you a proxy for it. Each call to this proxy will be handled by the Recycler and passed on to the original object (except calls to recycle).
If the called method returns a Domino object (i.e. an object that is compatible to
lotus.domino.Base), a proxy for the object is created and returned. The same is true for Vectors of
Domino objects. If the method that returned the Domino object is not named “getParent*”, the
object is assumed to be a child object. In this case the object's Proxy gets a reference to it's parent's
proxy. This keeps the parent from getting recycled before it's children.
The other way round, if you pass in a (Vector of) proxy/ies to a method, the Recycler finds the
associated Domino object(s) and substitutes it for the proxy/ies.
All Domino objects we created proxies for are kept as keys in a HashMap. This way we have strong
references to all the Domino objects. The associated values in the HashMap are weak references to
the proxies we've created. This way the proxies are subject to garbage collection as soon as they are
no longer used. In the next clean-up cycle the Recycler detects all weak references that are no
longer valid and recycles the associated domino objects. A clean-up cycle is performed each time
500 proxies were created. If the clean-up finds more than 5000 proxies that seam to be still in use it
calls System.gc() to perform a garbage collection to avoid running out of handles (if memory is
plentiful, garbage collection may not be frequent enough for our needs).
But the HashMap has another use: Whenever a new proxy is to be created Recycler first checks the
HashMap if there already is a proxy for this Domino object. If yes no proxy is created but the
existing is returned. This is needed because we can't register more than one proxy for the same
Domino object and to stay compatible: db.getView(“xy”)==db.getView(“xy”) is still true this way.
If we created a new proxy the equation would evaluate to false in opposite to operation without
Recycler.
Unfortunately there are a handful of single instance Domino objects. If recycled, they are lost and
gone. These are the objects returned by Session.getAgentContext(),
AgentContext.getCurrentAgent(), AgentContext.getDocumentContext and
AgentContext.getSavedData(). If you recycle the SavedData object it is lost and another call to
agentContext.getSavedData won't bring it back! Since beta 2 Recycler detects these objects and
keeps them alive until cleanup is called.
About the source: It is contained in the jar!

How do I contact you with feedback and questions?

The Recycler is an LGPL project by IT-Con Beratung und Service.
We're reachable by email:
Recycler(at)IT-Con.biz
Or, if you prefer “ordinary” mail:
IT-Con Beratung und Service
Zeche-Marie-Weg 11
34132 Kassel
Germany
Feel free to send bug reports, questions and feature requests.


MongoDB Logo MongoDB