Thursday, 8 January 2009

What is it about Garbage Collection?

I've been looking at some of the questions that people have been sending Bob Walsh to ask Nick Hodges (See my previous post) Apart from the clamour for a 64-bit compiler, by far and away the most popular question is "Why doesn't Delphi have garbage collection?" What is it about garbage collection that so excites the masses? Ok, you can create objects and forget about them. But seriously, Delphi developers, have you ever found it a major problem? I mean this isn't C++ (or God forbid C). If you're disciplined, and we're not talking military discipline here, then I don't see the problem.

I see all these comments like "With garbage collection, I can get on with solving the problem at hand rather than waste time managing memory."

How much of our time is wasted managing memory?

23 comments:

Jim McKeeth said...

I agree with you. Managing memory in Delphi is easy enough, and you can use Interfaces and other techniques if you need to get that memory managed for you. Plus I have experience working with C#, and its garbage collector is one of the best one out there, and you can still get memory leaks, the only problem is they are harder to fix when you do.

Garbage collectors are not the silver bullet. There is always some language feature that people expect to be this game changing event. It is either going to solve all development problems, or be the end of the the developer profession as we know it (like OOP when it came out).

The bottom line is the there are a lot of incremental change, but being a good software developer is still important. You can write really bad and really good code in any language.

\/\//\ Wim said...

Actually, Delphi does have a simplified "garbage collection" that will clean up objects once they're not used anymore. People never realize it, but in Delphi you can use interfaces. An interface is a special reference to an object. When the interface gets out of scope, Delphi will check if there are some other interfaces looking at the same object. If not, Delphi will automatically clean it.

Then again, too many people don't realize the power of interfaces. But basically, they're extremely useful. (As long as you keep things simple too.)

bose said...

Here is few examples, when garbage collection would be useful.
Have you ever seen similar code?
var
tmpSL1,tmpSL2,tmpSL3:TStringList
begin
tmpSL1 := TStringList.Create;
try
// do something with tmpSL1
tmpSL2 := TStringList.Create;
try
// do something with tmpSL2
tmpSL3 := TStringList.Create;
try
// do something with tmpSL3, tmpSL2 and tmpSL1
finally
tmpSL3.Free;
end;
finally
tmpSL2.Free;
end;
finally
tmpSL1.Free;
end;

almost forget, imagine that every try..finally block also contains some nice try..excepts. ;)
All these code, just to be sure that memory freed properly. It's really bore to write so much try..finally. Garbage Collection rules.

Oh, almost forget, there is another way to implement memory freeing.

var
tmpSL1,tmpSL2,tmpSL3:TStringList
begin
tmpSL1 := nil;tmpSL2 := nil; tmpSL3 := nil;
try
tmpSL1 := TStringList.Create;
// do something with tmpSL1
tmpSL2 := TStringList.Create;
// do something with tmpSL2
tmpSL3 := TStringList.Create;
// do something with tmpSL3, tmpSL2 and tmpSL1
finally
if tmpSL1<>nil then tmpSL1.Free;
if tmpSL2<>nil then tmpSL2.Free;
if tmpSL3<>nil then tmpSL3.Free;
end;

This one looks more compact than prevous, but... copy & pasting also, is not very funny way of programming.

Anonymous said...

Delphi does NOT need garbage collection. I imagine that the same people that clamor for it, or the ones that should be using a scripting language (or C#).

Bob said...

I agree. I think that it even goes to an attitude. Tossing your objects aside like empty candy wrappers indicates the level of discipline. Are you focused on all the process and paying attention to detail, or is code monkey beating the keyboard to earn his banana?
I don't think that it's the garbage collection per se, it's the coding philosophy behind the code.

Kaitnieks said...

I agree. If you don't feel like freeing your objects, you can always use interfaces.

Q++Studio said...

Garbage collection is like C's ability to declare local variable anywhere in a function. Makes it (slightly) faster to write code, but (much) harder to read later, even one's own code. I would argue that Delphi's TRY-EXCEPT-FINALLY is already a sort of "responsible" garbage collection.

Patrick said...

Amen!

Anonymous said...

Please, no garbage collection in native code Delphi. Use .Net based tools if this is a priority. Prism, I believe, does this plus more admirably.

Introducing garbage collection would remove the control I have over memory allocation, and this is one of the reasons I use Delphi native code compilers in the first place.

Babnik said...

Ahhh Interfaces. Now don't get me wrong, I use them all the time, but I have inherited code where the developer mixed interface and object references. Now THERE I wasted a lot of time!

Tjipke said...

bose: the checks for nil before free (like in if tmpSL1<>nil) can be left out. free does that check for you (that is the whole purpose of free, otherwise you could call destroy)

Alan said...

FastMM's ReportMemoryLeaksOnShutdown is great for finding memory leaks, so I don't quite see the purpose of GC. I think that instances like Bose's example are quite rare.

\/\//\ Wim said...

@bose, how about something like this:
var tmpSL1,tmpSL2,tmpSL3:IStringList;
begin
tmpSL1 := nil;tmpSL2 := nil; tmpSL3 := nil;
tmpSL1 := TStringList.Create;
// do something with tmpSL1
tmpSL2 := TStringList.Create;
// do something with tmpSL2
tmpSL3 := TStringList.Create;
// do something with tmpSL3, tmpSL2 and tmpSL1
tmpSL1 := nil;tmpSL2 := nil; tmpSL3 := nil;
end;

Assigning nil is not required but it informs the Delphi garbage collection to free the objects at that point, instead at the end of the procedure. Any exception would still invoke the garbage collector.
Only problem is that you need to use an interface instead of a class, which adds another level of complexity. Good programmers will be able to use it to their advantage. Bad programmers will turn all the code in just a single collection of garbage.

Joe White said...

I wondered the same thing. Then I worked on a project where I actually used a garbage-collected language. That's when I started to see why it matters. If you've never seriously used GC, you're not going to get it.

But I'll give one example: ever tried making a function that returns information in the form of a TStrings?

Babnik said...

Joe White : Good example, but hardly something that will waste half my day. And if you're really worried about it, return an interface.

Sean said...

So long as you think that garbage collection is about being lazy then you will never understand why people want it.

I wrote about this a year ago at http://sourceitsoftware.blogspot.com/2008/01/garbage-collection-in-delphi-win-32-why.html

The main reasons I have are
* Fewer memory leaks: By reducing the need for manually freeing memory, a gc significantly reduces the scope for memory leaks. It is still possible to leak memory, but much harder.
* Less code: I performed a naive analysis on my most recent project by removing most .Free calls and the supporting destructors and try … finally blocks. The result was about 4% fewer lines of code.
* Better code: In a non gc language, you end up with a number of idioms and practices to guard against memory leaks. Delphi has several of these.
Eg:
It is rare to return an object from a function. Typically you would create an object and then pass it to a procedure to be modified.
The use of assign rather than :=
The use of Owner, Owned and the like to solve object destruction problems)

* There are some problems that it is difficult to solve without a GC. Linq is often given as an example.

The main thing for me is that I can write better code with a gc than without.

GC is not suitable for all apps and situations, but neither is manual memory allocation.

It's my number one wanted feature for delphi.

Alan said...

I've used it quite a bit - the function creates a TStringList, the caller frees it, it's not particularly complex.

GC Now! said...

@Sean

You nailed one of the big ones. There is no convenient way for a function to return an object. How convenient it would be to have functions like:

function: GetImageSelectionAsBitmap: TBitmap;

function SelectedFileList: TStrings;

etc.

I know all you GC haters are going to bore me with 45 different ways you can handle this with the current code (I know them all too and don't need to be reminded. None of them are as convenient as the above).

X said...

Someone should just create a set of common interface descended objects, such as TGCStringList. That would address the GC needs in 99% of cases, and you can choose whether to use them or not.

Jarle Stabell said...

I agree completely with Sean.

For me, programming with a GC is more fun because my code becomes easier to understand and faster for me to write. It means less bureaucratic work for me, less constraints on the design and enables some powerful idioms/concepts.

In non-GC Delphi you need to know whether a given structured type is a record or a class or an interface as they all behave differently wrt memory management. You also need to be rather careful with cyclic interface based structures and when mixing classes and interfaces.

David N said...

It is hard the understand that advantage of GC until you use it, the same way it is hard to understand the advantage of something like a tool to "declare local var".

Sure, when making a new variable, you could always stop where you are, go the var section, find the end, declare it there, and then jump back to where you were and continue. Its not the end of the wold. But when you just hit CTRL+L on the new variable and it gets declared, you JUST don't think about it, and nor should you. You smoothly continue programming.

Considering you declare hundreds of variables per day when writing code, it adds up. People who do not use this will consider this a trivial, stupid example that "does not affect them" in their daily code. People who use this every day would never go back to jumping back and forth between the var section and their code 100x per day.

Same with GC. In Delphi we always have a background thought of, who owns this object, who will free it. How are we going to pass it around. If we pass it to someone, do they need to make a copy (i.e. assign) or can they just get a pointer etc etc. Then, when refactoring 6 months later, and don't remember exactly what you did, you have to check if your object just has a pointer or owns it, because you are not sure if you can free it. And you have to do this all the time for lots of objects.

When you are used to it, it does not seem like much effort. But I think it would certainly free up a lot of mental energy if this simply went away. It certainly has NOTHING to do with solving most of the problems I need to solve.

When allocating large chunks of memory or performing resource intensive tasks, you do want fine grained control over everything. But >90% of the time you do not care.

Anonymous said...

There are different tools for different tasks.

I have programmed C# for 5 years and I assure you, the loss of control in memory intensive projects is a big problem. If I could see the memory fragmenting, I could do something about it. In GC, I can't. In native Delphi, I decide what the memory is doing.

The convenience OOP provides between abstracting problem domain and hardware domain already comes at a cost. The convenience of returning objects is not worth the additional cost.

Compared to C/C++, the idioms offered by Pascal go a long way to preventing memory leaks. A tool, like the late TurboPower Software's Sleuth, would be really handy.

I think GC is adequately provided for in Prism. People that really need it are catered for. Please leave the fine control native code Delphi has as is.

Anonymous said...

Most of the gc advocates forget that managed code is a wrapper around unmanaged code. So it is very important to Dispose objects as soon as you are done with it. That it is not required but it is what make your application to scalle and perform better. Try this simple example: write small ASP.NET page with SQL connection and command and let GC deal with memory. Call it 1000000 times. Collect statistics on the server - CPU, I/O load and etc. Now create one that Disposes and assign NULL to the variables as soon as you are done. Do the test again. Compare statistics. Then you will see what is really important when writing programs.