Bill F posed a good question on StackOverflow about the issue of object.recycle and when to use it. It’s un understandable question and one I hit years ago after crashing a server looping through 20,000 documents in a search. I raised a PMR, got a response directing me to technotes on recycling in Java. Searching help, wikis and other places gave me no documentation on recycling in SSJS, which gave me reason to believe I was the first to hit the problem in XPages. So I blogged about it at the time. But the absence of SSJS documentation isn’t a huge issue for understanding the concept. SSJS runs Java, so all the Java documentation holds true.

The key thing to remember is the difference between Java memory issues and recycling issues. I think Nathan T. Freeman wrote a blog post about recycling handles, but I can’t find it at the moment. Lack of recycling doesn’t cause memory issues. Each time you get any Domino object, it gets / creates the C++ object and assigns a handle so it can retrieve it. So whatever the variable name, if you’re getting the same C++ object, I believe it’s the same handle. Recycling releases that handle. There are a limit to the number of handles, and that’s the importance of recycling.

But at the end of any process (a Java agent running, an XPage loading or the rendering of a partial refresh), the Session is recycled. The other important point to note is that recycling recycles all descendants. So recycling the Session recycles any Database that was retrieves, which recycles any View in that database that was retrieved, which recycles any ViewEntry in that database.

But column values can be a gotcha. If ViewEntry.getColumnValues() just contains text or numbers, it’s not a problem. They’re not Domino objects, so there’s nothing to recycle. However, if it contains dates, then there’s a big risk. It’s most apparent if we step through exactly what happens. Consider this code:

var myString = viewEnt.getColumnValues().get(0);

It looks straightforward, any many developers (myself included) have written code like that in a loop and thought no more about it. But let’s imagine the view contains has dates in the third column. Let’s see what happens in that one line of code.

  1. getColumnValues() is called. An empty Vector is created with the same size as the number of columns.
  2. The first column is retrieved. It’s text, so the value is put in the first element of the Vector.
  3. The second column is retrieved. It’s a number, so the double value is put in the second element of the Vector.
  4. The third column is retrieved. It’s a date, so a DateTime Domino object is created as a child of the Session. A C++ handle is assigned.
  5. The other columns are retrieved.
  6. The myString variable is set with the first element of the Vector.

At the end of the loop, if you’ve coded it correctly, viewEnt will be recycled. But that DateTime is a child of the Session, not the ViewEntry. So it won’t be recycled during the loop. Its handle will still be held for use, even though you never wanted to use it and never do use it. With the way most of us coded such loops, if we’re dealing with views with date columns, by the end of our code we’ll still have handles in use for all those DateTime objects; the total will be the number of entries iterated multiplied by the number of date columns in the view.

But you can’t recycle the Session – the parent of the ColumnValues Vector. You could iterate the Vector, get any DateTime objects and recycle the handles. Or you could use Session.recycle(Vector), as Nathan has covered in webinars. This does that job for you, recycling any handles to C++ objects in the Vector. But for that, you’ll need to change that one line of code first written to:

var myVect = viewEnt.getColumnValues();
var myString = myVect.get(0);
session.recycle(myVect);

You could do that.

But my preferred alternative is using the OpenNTF Domino API. Not only does it recycle DateTimes in Vectors and elsewhere for me, even if I didn’t know they should be, it also recycles anything else as soon as it can. Even without all the other functionality in the OpenNTF Domino API, that’s enough of a benefit for me, in reducing potential accidental server crashes and reducing the mount of typing I have to do.

10 thoughts on “The Perils of getColumnValues().get(0)”

  1. Thanks Paul. A problem that I have is that I can’t inforce that my clients use Domino API. I find a lot of Server Admins can get pretty anal retentive. I have come across some that wont install the OpenNtf extLib because it is not IBM. In any case from this post I have found another possible recycle issue and can solve that now.
    var myVect = viewEnt.getColumnValues();
    var myString = myVect.get(0);
    session.recycle(myVect);

    The assignment to myString allows the value to be returned and the vector recycled. Thanks again.

    1. I always find it baffling tat an admin would be happy to trust my non-IBM code on their servers, whether in an NSF or not. But would be wary of code from other experienced developers. I’d consider Nathan, Tim Tripcony, Jesse Gallagher, Roland Praml, Rene Winkelmeyer, Declan Lynch, Devin Olson and others who’ve helped develop the OpenNTF Domino API all brilliant developers. I prefer to write great functionality rather than waste my time fixing mistakes others have encountered and coded around already. Plus there’s no reason people can’t read or even extend the code.

  2. Since you mentioned them… http://nathantfreeman.wordpress.com/2013/03/21/recycle-and-the-retail-experience/ and the one with the Vector recycle is… http://nathantfreeman.wordpress.com/2013/07/03/a-funny-thing-happened-on-the-way-to-the-openntf-webinar/

    “So whatever the variable name, if you’re getting the same C++ object, I believe it’s the same handle.” Yes this is correct. And it’s actually pretty dangerous.

    The OpenNTF API considers DateTime and DateRange objects radioactive, and it gets rid of them as soon as possible. If you retrieve Item or Column values, or any object property that’s a DateTime, we convert it internally to a Java Date and immediately release the C++ handle. If we have to write it back to the underlying API, the C++ handle only exists for as long as it takes to write it back to IBM’s API and then it’s immediately recycled.

    Jesse, Roland and I have spent a lot of time getting this area of the API just exactly right and it’s been through over a year’s worth of production-level testing.

  3. A related note is that it at least used to be the case that fetching ColumnValues multiple times from an entry is surprisingly expensive, so fetching the property once at the start of the loop if you’re going to use column values at all is also more efficient.

    When I’m dealing with a legacy-API project, I have two main forms of the loop depending on whether or not I need to deal with date- or time-only DateTime values:

    When I don’t care about date- or time-only values, I convert to Java Date objects:

    ViewEntry entry = nav.getFirst();
    while(entry != null) {
    entry.setPreferJavaDates(true);
    List columnValues = entry.getColumnValues();

    // …

    ViewEntry tempEntry = entry;
    entry = nav.getNext();
    tempEntry.recycle();
    }

    When I DO care, I do what you mention and recycle the Vector at the end (and also have to declare the storage variable as Vector for method-calling purposes):

    ViewEntry entry = nav.getFirst();
    while(entry != null) {
    Vector columnValues = entry.getColumnValues();

    // …

    entry.recycle(columnValues);
    ViewEntry tempEntry = entry;
    entry = nav.getNext();
    tempEntry.recycle();
    }

  4. Jesse demonstrates something I forgot to mention: that the .recycle(Vector) call can be done from ANY lotus.domino object, not just the Session. Jesse uses a ViewEntry, but you could use the View, the Database, a Document, a Name, or even another DateTime to recycle the Vector itself. Session is simply one that you can be confident you’ll always have.

  5. I found this blog entry by searching for getColumnValues() limitations. I found a problem that’s just weird. I am trying to get DateTime values (lastUpdate) from a column in a view (currView) and see how many documents are newer than an arbitrary date (timeQuery). Here is the code snippet I wrote:
    var noOfNewItems = 0;
    var currentView:NotesView = database.getView(currView);
    var lastUpdateVector = currentView.getColumnValues(colNumber);
    for (var i=0, len=lastUpdateVector.length; i0) {
    noOfNewItems +=1;
    }

    }

    This code works most of the time, but it failed spectacularly with some larger views. If I print out the lastUpdateVector[i] values in the console, I get very strange results. For a while all dates are perfect, then it goes nuts and get dates like this (including empty lines):
    08/02/**** 11:31:19 AM MDT
    06/24/3679 09:45:34 PM MDT

    04/23/**** 08:34:23 AM MDT

    Am I doing something wrong? Can anyone explain what’s going wrong? Is there a 64k limit with getColumnValues(), too?

    Csaba
    PS: I tried to use View Navigator or Entry Collection and they were awfully slow.

  6. I am sorry the submitted code is missing some information. The blog deleted the code between the smaller and bigger operators. The loop should be like this:
    for (var i=0, len=lastUpdateVector.length; i *smaller* len ;I++) {
    if(lastUpdateVector[i].TimeDifference(timeQuery) *bigger* 0) {
    noOfNewItems +=1;
    }
    }

    1. I’ve not seen strange dates like that. Could they be time only elements? I’d recommend using TimeDifferenceDouble rather than TimeDifference. I hit the problem in LotusScript a few years ago where the output exceeded the size of the datatype used for TimeDifference.

  7. The main problem is that there’s nothing wrong with the dates in the view. However, getColumnValues() returns garbage without letting me know that there’s something wrong. I find that unacceptable. I did not find anything in the manual that would indicate that getColumnValues() has the same limitations as @Column.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.