Mega Code Archive

 
Categories / Delphi / System
 

Using TryFinally blocks to prevent memory leaks

Title: Using "Try/Finally" blocks to prevent memory leaks Question: How to avoid memory leaks by using "try/finally" memory gaurding blocks while creating and destroying an object at runtime. Answer: Using try... finally to prevent memory leaks When I first started with Delphi I found it surprising that many chunks of codes provided in tutorials used "try... finally" block without "catching" the exception. It did take some amount of reading to find out the reason and I am sharing it here with kindred spirits. Often "try... finally" statement is used to free the object regardless of whether an error has occurred in the "try" block. Maybe we should talk on component creation before we proceed. Most of the times you dont need to create components manually, you just drop it on a form and Delphi takes care of dirtier things like allocating and de-allocating memory. The components are created automatically when the form is created and destroyed with the form. But there are times when you would want to create components dynamically, most probably create non-visual components that are used locally. Components can be created by calling the component's constructor method and destroyed by freeing it (Do note that the owner is set to nil). Lets take a look at the following code: var myTable:TTable; begin myTable:=TTable.Create(nil); myTable.DatabaseName:='DBDemos'; myTable.TableName:='Employee.db'; myTable.Open; Label1.Caption:=myTable.FieldByName('FirstName').AsString; myTable.Free; end; This code works fine as long as no error is raised after myTable.Create and before myTable.Free but if there is an error, the process may terminate but the memory allocated during the Constructor call is not freed thus leading to access violation error. Memory leaks like these are nightmares and I mean it literally! However, this situation can be avoided by putting the code in "try... finally" blocks. The same code can now be written as var myTable:TTable; begin myTable:=TTable.Create(nil); try myTable.DatabaseName:='DBDemos'; myTable.TableName:='Employee.db'; myTable.Open; Label1.Caption:=myTable.FieldByName('FirstName').AsString; finally myTable.Free; end; end; Note I have not included the constructor within the "try" block, doing so will cause the code to try freeing the memory when the object itself has not been created. If an exception is raised during the execution of a constructor, the destructor is called automatically. However, if we put the constructor in the "try" block and "free" it in finally block and there is an error during the creation of the object, the memory is released but the "free" method will still be called in the "finally" block. This means we would have to face another access violation error. So myTable constructor should be called before "try" statement.(I mentioned this because I have seen at least one article advocating this 'wrong' approach. ) Now going back to access violations, another question is raised, why shouldnt we handle the exception and display a custom error message: After all we have a "try..." statement, so a catch statement (Except in Delphi) should follow before "finally" statement is called. Not really! Here is what Delphi 7 manual says "While it is possible to raise exceptions for almost any reason, and to protect almost any block of code by wrapping it in a try...except or try...finally statement, in practice these tools are best reserved for special situations." Delphi has a default exception handler to trap errors so it is not necessary to catch every exception by creating a handler. Any exception not specifically handled is ultimately caught and handled by the HandleException method of the global Application object. Besides adding an Except block would an overkill and may sometimes end up displaying two error messages to the user, the error message that we create and the error message inserted by Delphi. But I dont intend to say that there is no exception handling in Delphi. On the contrary, Delphi not only allows the developer to catch runtime errors and handle it in any manner, the Exception class declared in sysutils can be instantiated with a raise statement to generate an exception event to perform certain tasks. In fact, it would be prudent to handle exception in a "try... except" statement to perform alternative action. A nice way to handle exceptions, especially in database applications would be through the object events like OnPostError, OnDeleteError. As a matter of fact, while using TClientDataSet component, the ReconcileErrorDialog can be created on OnReconcileError event to respond to exceptions raised during the Reconcile method. Further, you can also create a global exception handler by coding the OnException event of the Application object. This event can handle any error raised during the execution of the code, I think its a good idea to handle exceptions in one block instead of coding them throughout the program. However, there are situations like validation of fields which need to be handled in the same block. Conditional statements like "if..else" statement are perhaps the simplest way to prevent/handle these errors if Edit1.Text:='' then begin ShowMessage('Please enter some text'); Exit; end else Of course how you handle errors would depend on the type of application you are developing, in my application which uses a TClientDataSet component I have used "if" statements and OnReconcileError event to handle most of the errors and exceptions. Anyway, I may be going off tangent since I set out to write on preventing memory leaks by using "try... finally" statements and not exception handling! So it is not necessary that every "try" block should be followed by an "except" block before "finally" block is called except when you want to trap expected errors and/or perform alternative action. Using "try.. finally" after object creation ensures that the allocated memory is freed regardless of whether the statements in the "try" block raised an error or not. I think its a very stoic way of handling errors (if anyone is interested in stoicism do visit my blog [http://o3.indiatimes.com/sofya/archive/2006/12/09/2911557.aspx]). So if you are coding operations like calling constructors which allocate memory, it would be advisable to put it before the "try" block and "free" the resource in the "finally" block to avoid those bothersome memory leaks.