Bob Balaban's Blog

     
    alt

    Bob Balaban

     

    Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    Bob Balaban  June 15 2009 10:41:32 PM
    Greetings, Geeks!

    I have written at length recently about garbage collection in both LotusScript and Java (see Geek-o-Tericas 3, 5, and 6 especially). I went on (and on...) about how and why you need to use the "recycle()" call in the Java classes for Notes, and why that call doesn't really exist in LotusScript, which has a more robust and automated gc mechanism integrated with the Notes classes.

    But, I neglected to mention the built-in LotusScript "delete" function, and since I've received 1 or 2 questions about it, I decided to do another (not so long, I hope) post on that topic.

    "Delete" in LotusScript is very close in function to recycle() in Java: it destroys an object instance, and causes it to be garbage-collected immediately. If the object you delete is a Notes back-end (OR front-end) class object, then all of the memory associated with that object is freed.

    Ok, that was easy! But the question remains: why would you ever need (or want) to use delete, when the LotusScript gc mechanism is invoked after every statement anyway?

    I have only ever needed it in one particular situation, having to do with (wait for it...) NotesAgent.Run() and NotesAgent.RunOnServer(). Why? Because there's a cute trick you can use to pass "parameters" to agents that you invoke from LotusScript, and, which you can also use to get back complex results from an agent. BUT, you need delete.

    So, let's say you have an agent that you want to invoke from a pice of LotusScript code. Maybe the agent is local, or maybe it lives on a server somewhere. The first thing you need to do is "navigate" your way to the agent. Typically you'd do that by using NotesSession.GetDatabase() to get the database containing the agent, then NotesDatabase.GetAgent() to get an instance of NotesAgent.

    If you want the agent code to execute on the same machine as the code that invokes it, you will be using NotesAgent.Run(). If the target agent lives on a server, you can use NotesAgent.RunOnServer() to have the agent code run on that server (your calling program waits for the agent to finish before resuming in both cases). For both methods, as of Notes v5.02, you may optionally supply the NOTEID of a "parameter" document. This comes in handy when you want to be able to pass runtime info to the executing agent to tell it specifically what to do. You just create a document instance (NotesDatabase.CreateDocument) -- locally for the Run() method, on the target server for the RunOnServer() method. You can put any data you want into the new document, obviously you have to know what the target agent is expecting.

    Then, you have to Save() the document object to disk. Why? Becuase you need a NOTEID (NotesDocument.NoteID) to pass to the agent, and a document object does not acquire a NOTEID until you've saved it. So then you can pass your newlly minted NOTEID to Run() or RunOnServer().

    The target agent is set up and invoked. That agent can find out what NOTEID was passed to it as follows:

       Dim s as New NotesSession
       Dim db as NotesDatabase
       Dim currentagent as NotesAgent
       Dim noteid as String
       Dim parameters as NotesDocument
       set currentagent = s.CurrentAgent
       noteid = currentagent.ParameterDocID
       set db = s.CurrentDatabase
       set parameters = db.GetDocumentByID(noteid)

    And off you go. Pretty convenient, huh? But we haven't even got to the REALLY cool part yet!

    What if your agent runs, and wants to pass a bunch of results BACK to the calling agent? You can! Just use the existing parameters document you already have. Your target agent can fill it up with whatever data you want to "pass back", and (of course) you have to call Save() on it again to get those changes committed to disk. Then, in your original calling agent, you have to get the modified version of that document back. You can't just use the NotesDocument instance you created to store your invocation parameters, because that document object still exists in memory,and it does not have your target agent's result data in it.

    THAT is why you need delete! Your calling agent has to delete the original parameter document, then re-fetch it with a NotesDatabase.GetDocumentByID() call (it will still have the same NOTEID it had before). Cool, huh?

    Does it work in Java? YES, with only one small difference: you don't get the "current agent" from the Session in Java, you get it from the AgentContext, which you get from the Session. Otherwise, this mechanism works the same in Java as in LotusScript.

    So, there's the one situation I've come across personally that requires "delete" (thanks to Daniel Lehtihet for suggesting this as a topic).

    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.      
    Comments

    1Bill  6/16/2009 3:14:15 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    We had a situation where we cacheed a huge amount of information in memory, and wanted to reduce memory consumption somewhat, and started adding delete's to our code.

    However, as it transpired, the memory objects we had created - lists of objects which contained lists of objects, all referring to each other - a huge hairball of interconnectedness - rather confused the garbage collector. The Lotusscript garbage collector. And it redboxed the client, crashed the server.

    We agree - only use Delete if you absolutely have to... In Lotusscript..

    ---* Bill

    2David Leedy  6/16/2009 7:04:54 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    Bob,

    Great post. I use this technique all the time when I need agents triggered by a user that does an ODBC connection to get data from our DB2 database on an iSeries.

    Doing it this way means we don't have to have custom ODBC settings on each users machine. As long as the server can connect it's good to go.

    3Rob McDonagh  6/16/2009 8:58:18 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    Hi Bob,

    We've got a pretty complex O-O routine, which also succeeded in crashing our server with regularity (like Bill's above). IBM support told us to add "Delete" all over the place, so we did. Ultimately, it didn't help. But I wondered why they SHOULD tell us to use Delete, when LotusScript is supposed to clean up after itself. Do you think there may be some known memory leaks in LS that aren't handled by the garbage collection but would be alleviated by using Delete? Or is IBM support confused?

    4Daniel Lehtihet  6/16/2009 9:32:03 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    My findings:

    Environment - Domino 7.03 (64bit) on AIX 5.3

    The delete statement does not matter for our problems (used a db with 100 000 documents where i updated every doc in a loop and dumped the memory usage every 500 iteration using the following code:

    Sub logMemUsage(s As notessession)

    Dim logdoc As notesdocument

    Dim db As notesdatabase

    Set db = s.GetDatabase("", "daniel/test.nsf")

    Set logdoc = db.CreateDocument

    Call logdoc.ReplaceItemValue("Form", "Log")

    Call logdoc.ReplaceItemValue("lsi50", Cstr( Lsi_info(50)))

    Call logdoc.ReplaceItemValue("lsi51", Cstr( Lsi_info(51)))

    Call logdoc.ReplaceItemValue("lsi52", Cstr( Lsi_info(52)))

    Call logdoc.Save(True, False, False)

    Set logdoc = Nothing

    Set db = Nothing

    End Sub

    )

    the lsi_info displayed a linear memory useage.

    I then took one agent that i suspect is to blame (it creates an xml-doc using notesstream and partly base64 encodes it using the built-in stream functions:

    Function base64Encode(session As notessession, doc As notesdocument, textToWrite As String) As String

    Dim stream As NotesStream

    Set stream = session.CreateStream

    Dim body As NotesMimeEntity

    session.ConvertMIME = False

    Call stream.WriteText(textToWrite)

    Set body = doc.CreateMIMEEntity

    Call body.SetContentFromText(stream, "text/html; charset=iso-8859-1", ENC_NONE)

    Call body.EncodeContent( ENC_BASE64)

    base64Encode = body.ContentAsText

    stream.Truncate

    End Function

    and sure enough, memory usage started to climb rapidly.

    The actual contextdoc we are dealing with stores its content as mime (we use the xhtml output when we later on call Domino from WebSphere to render PDF-files using the ITEXT open source library (all this happends on the WAS side of things tough...).

    when i comment everything besides the mem.dump stuff, memory consumption goes back to a linear mode again.

    Even if i only have the variable declarations in there, memory climbs again.

    I am puzzled.

    Does the LS garbage collector work differently on different OS:es and platforms? Does the 64-bit unix environment (with its different memory model) come into play here?

    Kind regards

    Daniel

    5Brent Henry  6/16/2009 9:49:00 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    Bob,

    I've been using this technique a lot lately in order to pass parameters to an agent that generates a lot of xml.

    I'm using Set doc=Nothing rather than deleting it. Is this functionally equivalent or is there a difference?

    I am also using this technique from a VB.NET application that uses COM to create the document and pass it to the agent. Objects do not appear to be destroyed the same when COM is added to the mix.

    When I Set doc=Nothing (can you call Delete using COM?) and then retrieved the document using session.resolve (notesurl), I could see the new rtitem in the Visual Studio debugger but it did not have the correct data type. drilling down on it to see its value would cause Notes to crash.

    In the end I used a function call that instantiated a new Domino.NotesSession object in order to see my updated document.

    Thanks for posting on this. It's hard to find this info anywhere else.

    6Bob Balaban  6/16/2009 11:15:32 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    Greetings, Geeks! Wow, who'd have thought that such a Geekoteric topic would get so many quick esponses.

    Here are some additional comments:

    @3 and @5 - This brings up the question of "set x = nothing" vs. "delete x". I probably should have expanded on that in the post, but here's the summary:

    They're the same ALMOST all of the time, but not always. For example, if you have:

    Dim x1 as new NotesDocument(somedb)

    Dim x2 as NotesDocument

    set x2 = x1

    You have 2 variables which reference the same underlying Document object. "Delete x1 (or x2)" says to LotusScript: "Destroy this object RIGHT NOW!". The Document object is gc'ed and both variables are now "nothing".

    On the other hand, if you start with the above and do "set x1 = nothing", x2 STILL points to a valid Document object. Overwriting the object reference in x1 with "nothing" simply decrements the reference counter that LS maintains. Since there is still another valid object reference in x2, the object is still in use, and is not destroyed.

    I tend to like the "set x = nothing" way of doing things. It speeds up gc a bit on things that haven't gone out of scope yet, and it limits unwanted side effects.

    Rob -- LS is pretty well debugged by now (13 years later), but memory leaks are always a possibility. I would never, ever claim that my hard-working friends in Lotus Support are "confused". But I think they themselves would admit that they don't

    always have all the info they need to offer fully qualified advice to customers. Of course, they do the best they can with what they have/know, and most times they do an excellent job.

    Brent -- COM is a whole different ball of wax. The COM classes run in some Visual Studio runtime environment, LotusScript is completely out of the picture. I think (not sure, would have to check the doc) that there's a Release() method that's about the

    same in COM as "set x = nothing" in LS.

    @4 - Daniel, haven't had time to examine your code sample. When I get to it, I'll email you.

    7John Smart  6/20/2009 1:24:36 PM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    Another reason: code reflection issues. In my experience, error trapping and reporting (via OpenLog or other) during the auto-cleanup after agents finish sometimes yields unexpected results, so when I've written a class with a delete sub I usually find it best to delete those objects explicitly at the end of an agent's initialize.

    8Bob Balaban  6/21/2009 7:42:40 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    @7 - John, are you referring to Java or LotusScript? Logging during auto-cleanup from an agent is problematic, because the log object itself needs to get gc'ed too....

    9John Smart  6/25/2009 2:28:22 PM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    @8 - Bob, LotusScript. And we agree. For example, say I have a (MVC-style) model object that, on delete, logs a warning message if there are unsaved changes so that I and others on my team developing can easily notice if we've made a mistake. If I delete the objects explicitly then this is fine, but strange results happen if I let the automatic garbage collection do it.

    10Rami Jundi  7/8/2011 9:28:32 AM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    What is your take on Custom Classes in LotusScript and Delete? It gives the ability to write your own Delete() method. I wrote a custom class that is implemented as List with one of its members being another List of Objects that range from 1-30 items.

    Should I implicitly Delete(erase) the list within the list inside a custom object when finished. Or does LotusScript clean that up for me?

    Thanks

    11Bob Balaban  7/8/2011 8:36:14 PM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    I think lotusscript will automatically garbage collect all unreferenced objects

    12Ravindra MN  6/10/2012 11:05:25 PM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    This helped when i get struck in coding. It not because of my fault in code but the insufficient use of memory... Can you Give the much more detail on the Object release

    13Bob Balaban  6/13/2012 3:29:31 PM  Geek-o-Terica 8: LotusScript, To Delete, or Not to Delete?

    Sorry, I'm not understanding your question. Can you be more specific?