http://www.johndcook.com/blog/2011/06/14/why-do-c-folks-make-things-so-complicated/
It proposes splitting C++ into two sections, effectively- a "Bottom" C++ of manual memory management, and presumably pointers and all those other terrible things, and "Top" C++, of garbage collection, and presumably, we can ditch headers and macros and all that as well. Let us celebrate.
Except, I don't genuinely believe that garbage collection is higher than scope-based resource management. In fact, I think that it's lower level than C++'s scope-based resource management. The trouble with garbage collection is that it's only memory. Any other resource, and you're back to
malloc
and free
. And only memory that you allocated from the garbage collector, too. That's the fundamental problem- garbage collectors can only handle their own, pre-programmed situations.Consider the automatic memory management in C++0x -
unique_ptr
. I wrote a trivial custom deleter (five lines) and now it will automatically manage my COM objects where I want to tie the lifetime of my reference. The same works with shared_ptr
- I can just plug in a custom deleter and get shared ownership of my reference. Not only will I be able to share ownership of regular memory, but I'll also be able to share ownership of, well, whatever I want.If, in C#, I want shared ownership or unique ownership of GC-allocated memory- that's great, the GC will handle that for me. But what happens if I want shared ownership of a deterministically destructed resource? Well, now we have a big problem. because the resource class itself has to be Disposable. And you'll have to implement your own reference counting- including thread safety. That's fine- but every class that needs to hold an instance of that class also needs to be Disposable and Disposed. And they don't call themselves- you will have to Dispose() manually in every situation except where the Disposable object is on the function local stack. How is this any different to C-style
free()
?In addition, I've really got to question the ability of a garbage collector to actually manage memory. Now, I've heard of GC/JIT optimizations that will put objects on the native stack. But I've certainly never heard of a JIT or GC that will make an object pool for you. In managed languages, people still implement object pools, just like there are object pools for C++. Is this really automatic memory management?
Let's talk about contiguous memory. Can I ask the GC to allocate me some nice, contiguous memory? Sure- if I can allocate all at once. But can I ask it to store some contiguous memory, and then take objects in and out of it as I see fit, and refer to those objects? Well- no. Once I allocate that array, it's done. This isn't so bad for something that's trivially copyable like an int, but once we start talking about non-copyable classes, for example, then bad things happen. This is especially bad as you can only have contiguous arrays of value types in C# - something that doesn't even exist in Java- and I sure hope I didn't want to store references to value types, instead of to a copy. If we're doing a lot of work on these types, not being able to store them continuously is an incredible performance hit.
So let's summarize. We have on the left side a system that can automatically free any resource, in shared or unique ownership conditions, with a high degree of customizability and performance. On the right side, we have manual resource freeing, no customizability, and degraded performance.
I'm going to ask- why would anyone consider the system on the right to be "higher level"?
Frist!
ReplyDeleteWow, it turned out not to be a rant after all :)
> you will have to Dispose() manually in every situation except where the Disposable object is on the function local stack.
Even if it's in the local stack you have to call it manually. The runtime is completely oblivious of Dispose().
Wow, rant rant!
ReplyDeleteI think that higher level is really just a matter of abstractions.
GC is more about what level of control you have over memory, and some prefer more control then others.
@Martinho: There is using(), which will call it for you at least somewhat automatically.
ReplyDeleteBut that's just another *manual* way of calling Dispose (and incidentally the preferred one). If you don't do it, the runtime won't.
ReplyDeleteYou can hook into the finalization process with a C# destructor, but that will leave your resources open until the GC decides to clean it up. And most of the time, you want to release them as soon as you're done.