Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents

Part I - Foundations
  Chapter 1 – Delphi 7 and Its IDE
  Chapter 2 – The Delphi Programming Language
  Chapter 3 – The Run-Time Library
  Chapter 4 – Core Library classes
  Chapter 5 – Visual Controls
  Chapter 6 – Building the User Interface
  Chapter 7 – Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 – The Architecture of Delphi Applications
  Chapter 9 – Writing Delphi Components
  Chapter 10 – Libraries and Packages
  Chapter 11 – Modeling and OOP Programming (with ModelMaker)
  Chapter 12 – From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 – Delphi's Database Architecture
  Chapter 14 – Client/Server with dbExpress
  Chapter 15 – Working with ADO
  Chapter 16 – Multitier DataSnap Applications
  Chapter 17 – Writing Database Components
  Chapter 18 – Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 – Internet Programming: Sockets and Indy
  Chapter 20 – Web Programming with WebBroker and WebSnap
  Chapter 21 – Web Programming with IntraWeb
  Chapter 22 – Using XML Technologies
  Chapter 23 – Web Services and SOAP
  Chapter 24 – The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 – Delphi for .NET Preview: The Language and the RTL
       
  Appendix A – Extra Delphi Tools by the Author
  Appendix B – Extra Delphi Tools from Other Sources
  Appendix C – Free Companion Books on Delphi
       
  Index    
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

 
Previous Section Next Section

Garbage Collection

The garbage collector is that part of the CLR that performs automatic management of memory allocation and deallocation. All Delphi 7 offers in this respect is reference counting for interface-based variables (see Chapter 2 for details). When there are no more references to an object, the memory for it is reclaimed. This is also the basic idea of the CLR garbage collector, but the similarities stop there. The CLR's garbage collector can detect that two objects are still referring one to the other, but there is no other reference to them, so they can be discarded, something a reference counting system (like the one in Delphi) fails to handle.

Using a GC means you can create objects, reference one from another as you need, and simply forget about the hassles of deleting those objects. The system will do all that's needed for you. That's all you really need to know. Note that this implies that you don't need to balance object creation and destruction in your code architecture, nor do you need to use try/finally blocks to make sure objects are destroyed; these are just a couple of specific circumstances in which you'll benefit from a GC.

Only when you need to write low-level classes will you have to remember to free unmanaged resources, such as window or file handles. The CLR class System.Object contains a protected Finalize method that you can override to free up unmanaged resources. You must be aware of some important things before you begin overriding Finalize. (If you want some in-depth information on this topic, see the sidebar "Issues with Overriding Finalize.")

The recommended approach for freeing up unmanaged resources is to implement the dispose pattern. This is a strategy for providing an object both an automatic way to dispose the external resources it refers to when it is garbage collected and also a way to free the same resources upon the direct invocation of a method. In real terms, coding this pattern equates to implementing an interface called IDisposable. IDisposable consists of one method: Dispose. The IDisposable interface gives you deterministic control over freeing up resources used in your objects. Unlike Finalize, the Dispose method is public and is meant to be called by you (or users of your class), not by the garbage collector.

You may have read .NET literature explaining how the C# language implements destructor semantics by having the compiler create a Finalize method on your class behind the scenes. To implement the IDisposable interface in C#, you must do so directly and wire everything so that the Dispose method can be called directly or from the automatically generated Finalize method. This is not the way things work in Delphi for .NET.

Creating a destructor for your class does not cause the compiler to implement a finalizer behind the scenes. Instead, it causes the compiler to implement the IDisposable interface. But nothing is stopping you from implementing IDisposable directly, so if you want to rely on the compiler's behind-the-scenes magic, you must follow a strict pattern. Your class destructor must be declared exactly as shown here:

destructor Destroy; override;

When the compiler sees this declaration, it generates code to mark your class as an implementor of IDisposable. Then, using a feature of CIL, your destructor is marked as the implementation of the Dispose method (this can be done even though the name of the method is Destroy, not Dispose).

The usual way to dispose of objects in Delphi is to call the Free method on the object. In Delphi for .NET, the Free method is implemented so that it tests to see whether the object implements the IDisposable interface. If you follow the previous destructor signature, the compiler implements it for you; Free then calls Dispose, which winds up in your Destroy method.

Let's create a project and declare a class with a destructor that follows the pattern. Then you'll use ILDASM to inspect the generated code. The code is available in Listings 24.1 and 24.2.

Listing 24.1: The Project of the DestructorTest Example
Start example
program DestructorTest;
   
{$APPTYPE CONSOLE}
   
uses
  MyClass in 'MyClass.pas';
   
var
  test : TMyClass;
   
begin
  WriteLn ('DestructorTest starts');
  test := TMyClass.Create;
  test.Free;
  WriteLn ('DestructorTest ends');
end.
End example
Listing 24.2: The Unit of the DestructorTest Example
Start example
unit MyClass;
   
interface
   
type
  TMyClass = class
  public
    destructor Destroy; override;
  end;
   
implementation
   
destructor TMyClass.Destroy;
begin
  WriteLn ('In destructor (which is actually the IDisposable.Dispose method)');
end;
   
end.
End example

After compiling this program you can run it; but we are not so much interested in running the program as in inspecting it with ILDASM. Start ILDASM and select File | Open. Navigate to the directory where DestructorTest.exe is located, and open it. You will see a window similar to that shown in Figure 24.2.

Click To expand
Figure 24.2: The ILDASM output for the DestructorTest example

The tree entry labeled MyClass represents the namespace created to hold all the symbols in the unit. Expand the MyClass node and notice the entries for TMyClass and Unit. Delphi for .NET creates a CLR class for each unit to implement initialization and finalization and also to let you still write global routines; they become methods of that Unit class. Expand the node for TMyClass and notice the Destroy node, which has a pink block. This node represents the Destroy method in TMyClass. Double-click the Destroy node to open a window displaying the full IL code for this method, as shown in Figure 24.3.

Click To expand
Figure 24.3: The ILDASM IL window for the Destroy destructor

The line you're looking for uses the .override directive:

.override [mscorlib]System.IDisposable::Dispose

This is an explicit override, and it says that the Destroy method is the implementation of Dispose in the IDisposable interface. The .override directive is how the thread of execution winds up in your destructor when Dispose is called from Free.

Note 

The way IDisposable was dealt with illustrates a familiar pattern in the ongoing work on the Delphi for .NET compiler. Unlike C#, Delphi is an old language with a large and loyal following and a sizable existing code base. Borland could not sweep everything aside and mandate a new way of doing things that would break all existing code. The implementation of IDisposable was done in a way that should allow Delphi programmers to continue using familiar paradigms and minimize the impact of porting their existing code to the .NET platform.

Garbage Collection and Efficiency

Among programmers, the use of a garbage collector (GC) is the topic of many debates. Most programmers like the fact that a GC helps them reduce the chance of memory management errors. Some programmers, though, fear that a GC might not be efficient enough, a fear that often prevented the widespread use of this technology. The inefficient GC of the early Java virtual machines didn't really help in this respect. Even Microsoft has a hard time convincing all programmers to use its GC, up to the point that they overrate their solution depicting it as perfect. Even in Microsoft technical documentation about the GC, you'll find lots of hype and little facts.

Note 

This is not to say that the GC doesn't do an adequate job; quite the contrary. But it works differently than it is presented. At present, your only way to know how the GC works is to write test case programs and study their effect.

As a starting point for your exploration, you can use the GarbageTest example. Use a Windows memory analysis tool to see how the memory is affected by program execution. The GarbageTest example declares the following class of large objects (about 10 KB):

type
  TMyClass = class
  private
    data: Integer;
    list: array [1..10240] of Char;
    s: string;
  public
    constructor Create;
  end;

The simplest test code is the following:

for i := 1 to 10000 do
begin
  mc := TMyClass.Create;
  WriteLn (mc.s);
end;

Writing this simple program in Delphi will flood the memory, because there is no call to Free. In .NET, however, the GC reuses the single memory block, so memory consumption is flat. If you save each object in an array declared as

objlist: array [1..10000] of TMyClass;

the memory consumption will increase at each cycle of the loop, causing problems! Keeping the object references in memory and then releasing them regularly or randomly can provide more complex tests. You'll find a few snippets in the program code, but you'll have to exercise your knowledge and imagination to build other significant test cases.


 
Previous Section Next Section


 


 

Delphi Sources


Copyright © 2004-2024 "Delphi Sources" by BrokenByte Software. Delphi Programming Guide
ร๐๓๏๏เ ยส๎ํ๒เ๊๒ๅ   Facebook   ั๑๛๋๊เ ํเ Twitter