Bob Balaban's Blog

     
    alt

    Bob Balaban

     

    Geek-o-Terica 3: Taking out the garbage (LotusScript)

    Bob Balaban  April 27 2009 04:56:24 AM
    Greetings Geeks!

    LotusScript is a cool language. One of the reasons it's cool is a feature called "automatic garbage collection". What does that mean? It means that you can litter your code landscape with snippets of memory to your heart's content, and never have to worry about keeping track or picking it up ("deallocating", or "freeing" the allocated memory chunks) later.

    It means you can code a loop like this (skipping all the DIMs, but you get the idea):

     set view = db.GetView("$all")
     set doc = view.GetFirstDocument()
     while not (doc is nothing)
          ' do something...
          set doc = view.GetNextDocument(doc)
     wend

    Every time you do "set doc = ", the Notes back-end classes infrastructure is generating a new NotesDocument object in memory. What happens to the object that used to be stored in the "doc" variable? LotusScript automatically keeps track of all references to objects, and when there are none (in this case, when you assign a new object reference to "doc", overwriting the old one), the memory used by that object is reclaimed.

    When does garbage collection happen? It happens in LotusScript after the execution of every statement (which can lead to some interesting side-effects, as we'll see in a minute). It wasn't always like that, though. In Notes V4.0 (the first appearance of LotusScript in the product), garbage collection only happened at the end of each Sub or Function. But that wasn't good enough, because, as in the above snippet, if the View had, say, 100,000 documents in it, you'd run out of memory before you got to the end of the function.

    But, as with all great boons to humanity, automatic garbage collection in LotusScript has its dark side. Consider this:

     dim s as new NotesSession
     dim entry as NotesEntry
     set entry = s.CurrentDatabase.ACL.GetFirstEntry

    Seems innocuous, right? But you'll never get an object in "entry". Why? Because there's another rule I haven't reminded you of yet (you probably already knew it, but you didn't realize it could bite you like this in some cases). The rule is that when an object is garbage-collected, all of its child objects are also garbage-collected.  That might seem needlessly destructive, but it's actually necessary. Notes does not allow free-floating objects that exist outside their container, or "owner" object. Documents, for example, must belong to a database. Items must belong to a document, and ACLEntries must belong to an ACL object.

    So, what really happens in the above snippet? The expression "s.CurrentDatabase.ACL.GetFirstEntry" is perfectly legal in an agent, where the NotesSession will always exist, as will the CurrentDatabase. And we know that every Notes database has an ACL object with at least one ACLEntry in it. So the expression will always evaluate and create an ACNEntry object, which then gets assigned to the "entry" variable.

    But then, once that all happens, garbage collection kicks in. It says, "What can I sweep up that is no longer referenced?" Session and CurrentDatabase are special, they have to stay around until the agent is done. But, AHA! The ACL object isn't used anymore after this statement is done (the object reference is not saved in any local variables). So it gets destroyed. And, invoking the rule above, all of its instantiated children also get destroyed, including the ACLEntry instance that we just saved in "entry". All before the next statement is executed. Easy come, easy go.

    Everything would have been fine if we had coded the above thusly:

     dim s as new NotesSession
     dim entry as NotesEntry
     dim acl as NotesACL
     set acl = s.CurrentDatabase.ACL
     set entry = acl.GetFirstEntry

    This way, the NotesACL object hangs around until the Sub exits, and so our ACLEntry object is also preserved.

    Now consider this bit of logic:

     set item = doc.items(0)

    Do we have a similar problem here? Will "item" always be Nothing? Actually, no, this works. It's true that the array of NotesItem objects created by the "doc.items" property is garbage-collected, but the single NotesItem object we saved in "item" is fine. Why? Because the item is "owned" by the NotesDocument object, not by the array. The array disappears, but the document doesn't.

    Of course you would be making a mistake to code a loop such as this:

     for i = 0 to ubound(doc.items)
        set item = doc.items(i)
        ' do something....
     next i

    This is horribly inefficient, because every invocation of "doc.items" above will create an array, and then populate that array with a set of NotesItem objects. Then at the end of each statement, the array will be garbage-collected, along with all but one of the NotesItem objects. That's a lot of work to do over and over. Much better to access the array one time, save it in a Variant variable, and then iterate over the array.

    Next time: How garbage collection in Java is way different.
    Geek ya later!

    (Need expert application development architecture/coding help? Contact me at: bbalaban, gmail.com)
    Follow me on Twitter @LooseleafLLC
    This article ┬ęCopyright 2009 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.