I read Bryan Flamig's criticism of C# via Loren. Bryan voices a common criticism of C#'s garbage collection (the criticism actually would apply to all languages that don't go beyond the default memory management model of CLR, but let's just continue to use C# as the example): in C++, the timing of when an objects is "cleaned up" is under program control (it's fully deterministic), while in C#, the default behavior is that the timing is non-deterministic (in fact, reclaiming memory is an aspect of "cleaning up" whose precise timing is very explicitly beyond the control of the developer).
Bryan makes the excellent point that "cleaning up an object" certainly means releasing the memory for reuse but can also mean closing a network socket, database connection, file handle, etc. There are many examples of valuable resources such as these that come from a limited pool, and it's perfectly understandable for a programmer to want to control the timing of when such resources are "put back on the shelf," as it were.
In C++, releasing resources and releasing memory are thought of as one event. In C# and most other .NET languages, the act of cleaning up is thought of as being two events: the release of valuable resources (finalization) and the release of memory (destruction). By default, the CLR's garbage collector guarantees that it will call a "finalizer" function prior to releasing an object's memory. So, if you use the finalizer method to control your valuable resources and do nothing else, "cleaning up" is linked to the non-deterministic garbage collector. That's the chief criticism of C#'s garbage collection.
But, to be fair, these non-memory resources are not what garbage collection aims to solve. Garbage collection aims to solve managing physical memory. Allocating and deallocating memory has unique problems (in that memory can point to other memory) and characteristics (the ratio of memory allocations and deallocations to, let's say, the number of file handles opened and closed is approximately a zillion to one). So relying on an algorithm for garbage collection based on physical memory to time the clean-up of your valuable resources is a questionable strategy. But that doesn't mean that garbage collection (of physical memory) is not a great advantage: the garbage collector is fast, it compacts the heap, and it goes a long way to help the most common types of programming bugs in C and C++. It doesn't solve memory management, but it helps a lot.
So if you don't want to rely on the garbage collector to trigger your finalizer to release your valuable resources, what do you do? You implement IDisposable and put the release of valuable resources entirely under programmatic control. Sure, that's an error-prone burden, just like managing physical memory was. But it's a start.
Now, can one imagine a CLR that had an algorithm that was optimized for managing non-memory resources? Yeah, but it might have poor performance. Or can one imagine some in-language facility that provides "deterministic finalization"? Yeah -- C++/CLI will have such a thing and it's certainly on the radar of the designers of the C# language.
But memory management via garbage collection? .NET provides a great implementation of a great idea.