views:

288

answers:

10

I was reading the C# entry on Wikipedia, and came across:

Managed memory cannot be explicitly freed; instead, it is automatically garbage collected.

Why is it that in languages with automatic memory management, manual management isn't even allowed? I can see that in most cases it wouldn't be necessary, but wouldn't it come in handy where you are tight on memory and don't want to rely on the GC being smart?

+1  A: 

I can't say that it is the answer, but one that comes to mind is that if you can free, you can accidentally double free a pointer/reference or even worse, use one after free. Which defeats the main point of using languages like c#/java/etc.

Of course one possible solution to that, would be to have your free take it's argument by reference and set it to null after freeing. But then, what if they pass an r-value like this: free(whatever()). I suppose you could have an overload for r-value versions, but don't even know if c# supports such a thing :-P.

In the end, even that would be insufficient because as has been pointed out, you can have multiple references to the same object. Setting one to null would do nothing to prevent the others from accessing the now deallocated object.

Evan Teran
Nulling the passed pointer isn't a solution to anything. Pointers can be duplicated, and so dereferenced by other code. See my answer.
Novelocrat
of course, i didn't try to imply that it was an effective solution.
Evan Teran
+1  A: 

If you are in situation that you "don't want to rely on the GC being smart" then most probably you picked framework for your task incorrectly. In .net you can manipulate GC a bit (http://msdn.microsoft.com/library/system.gc.aspx), in Java not sure.

I think you can't call free because you start doing one task of GC. GC's efficiency can be somehow guaranteed overall when it does things the way it finds it best and it does them when it decides. If developers will interfere with GC it might decrease it's overall efficiency.

Andrey
It's primarily about safety, not efficiency.
Novelocrat
Though GC can often improve performance, very few systems use collectors with provable properties, because they pay a large cost in overhead to maintain those guarantees.
Novelocrat
@Novelocrat i don't agree about safety. i think it is about integrity of GC. "Give to Caesar what is Caesar's", let GC do it's job. .net/java still gives you ability to shoot at your foot.
Andrey
A: 

Manual management is allowed. For example, in Ruby calling GC.start will free everything that can be freed, though you can't free things individually.

Evgeny Shadchnev
That's not really the same thing.
Michael Myers
A: 

Interestingly enough, you do have access to the garbage collector through System.GC -- Though from everything I've read, it's highly recommended that you allow the GC manage itself.

I was advised once to use the following 2 lines by a 3rd party vendor to deal with a garbage collection issue with a DLL or COM object or some-such:

// Force garbage collection (cleanup event objects from previous run.)
GC.Collect();         // Force an immediate garbage collection of all generations
GC.GetTotalMemory(true);

That said, I wouldn't bother with System.GC unless I knew exactly what was going on under the hood. In this case, the 3rd party vendor's advice "fixed" the problem that I was dealing with regarding their code. But I can't help but wonder if this was actually a workaround for their broken code...

Pretzel
Probably was. The one case where i'd ever consider calling GC.Collect is if i had a huge, long-lived object i'd just discarded. The GC assumes if an object has been around for a long time, you're gonna keep it a while longer, so anything that's survived collection a bunch of times won't be collected unless a full collection is done. That said, it's a rare case that requires you do it manually.
cHao
That's not manual memory management.
Novelocrat
@Novelocrat: I never said it was. All I said was "Interestingly enough..."
Pretzel
@cHao: It seems you understand what's going on "under the hood" and you knew when to use GC (and obviously, not to abuse it...) :-)
Pretzel
+1  A: 

Calling GC.Collect is almost always the better than having an explicit free method. Calling free would make sense only for pointers/object refs that are referenced from nowhere. That is something that is error prone, since there is a chance that your call free for the wrong kind of pointer.

When the runtime environment does reference counting monitoring for you, it knows which pointers can be freed safely, and which not, so letting the GC decide which memory can be freed avoids a hole class of ugly bugs. One could think of a runtime implementation with both GC and free where explicitly calling free for a single memory block might be much faster than running a complete GC.Collect (but don't expect freeing every possible memory block "by hand" to be faster than the GC). But I think the designers of C#, CLI (and other languages with garbage collectors like Java) have decided to favor robustness and safety over speed here.

Doc Brown
GC environments can actually be higher performance than the equivalent manual environment, because allocation is often just bumping a pointer. Moving collectors can also improve locality of reference as the program runs.
Novelocrat
Also, GC in the JVM and CLR are not reference-counting based, because that doesn't account for cyclic structures. They trace from a root set and either mark or move objects that are reached, disposing of objects that are never reached.
Novelocrat
@Novelocrat: you are right what you say about JVM and CLI, I should have said about "reference monitoring", not "reference counting". However, I intentionally wrote "calling free for a single memory block", not "calling free to duplicate the hole GC behaviour".
Doc Brown
+10  A: 

Languages with automatic memory management are designed to provide substantial memory safety guarantees that can't be offered in the presence of any manual memory management.

Among the problems prevented are

  • Double free()s
  • Calling free() on a pointer to memory that you do not own, leading to illegal access in other places
  • Calling free() on a pointer that was not the return value of an allocation function, such as taking the address of some object on the stack or in the middle of an array or other allocation.
  • Dereferencing a pointer to memory that has already been free()d

Additionally, automatic management can result in better performance when the GC moves live objects to a consolidated area. This improves locality of reference and hence cache performance.

Novelocrat
in .net calling Dispose incorrectly can create same problems.
Andrey
Only if the Dispose implementation is badly coded
thecoop
+2  A: 

Garbage collection enforces the type safety of a memory allocator by guaranteeing that memory allocations never alias. That is, if a piece of memory is currently being viewed as a type T, the memory allocator can guarantee (with garbage collection) that while that reference is alive, it will always refer to a T. More specifically, it means that the memory allocator will never return that memory as a different type.

Now, if a memory allocator allows for manual free() and uses garbage collection, it must ensure that the memory you free()'d is not referenced by anyone else; in other words, that the reference you pass in to free() is the only reference to that memory. Most of the time this is prohibitively expensive to do given an arbitrary call to free(), so most memory allocators that use garbage collection do not allow for it.

That isn't to say it is not possible; if you could express a single-referrent type, you could manage it manually. But at that point it would be easier to either stop using a GC language or simply not worry about it.

MSN
This is a good description of the memory safety I mention in my answer. Thanks.
Novelocrat
A: 

Many of the other answers provide good explanations of how the GC work and how you should think when programming against a runtime system which provides a GC.

I would like to add a trick that I try to keep in mind when programming in GC'd languages. The rule is this "It is important to drop pointers as soon as possible." By dropping pointers I mean that I no longer point to objects that I no longer will use. For instance, this can be done in some languages by setting a variable to Null. This can be seen as a hint to the garbage collector that it is fine to collect this object, provided there are no other pointers to it.

svenningsson
I won't downvote - but in some systems (e.g. .NET), the JIT/GC collude to keep track of live references within method bodies. This means that as soon as the variable is no longer accessed, whatever it's pointing to is eligible for collection (presuming no other outstanding references). Setting the variable to null actually *prolongs* the lifetime of the variable.
Damien_The_Unbeliever
@Damien - a compiler or runtime that ran its analysis on a Static Single Assignment representation of the program would not have a problem with the assignment of nulls. In fact, if that assignment post-dominated the original assignment, then it offers a hard boundary to the latter's lifetime.
Novelocrat
A: 

In systems that allow objects to be manually freed, the allocation routines have to search through a list of freed memory areas to find some free memory. In a garbage-collection-based system, any immediately-available free memory is going to be at the end of the heap. It's generally faster and easier for the system to ignore unused areas of memory in the middle of the heap than it would be to try to allocate them.

supercat
A: 

Why would you want to use free()? Suppose you have a large chunk of memory you want to deallocate.

One way to do it is to call the garbage collector, or let it run when the system wants. In that case, if the large chunk of memory can't be accessed, it will be deallocated. (Modern garbage collectors are pretty smart.) That means that, if it wasn't deallocated, it could still be accessed.

Therefore, if you can get rid of it with free() but not the garbage collector, something still can access that chunk (and not through a weak pointer if the language has the concept), which means that you're left with the language's equivalent of a dangling pointer.

The language can defend itself against double-frees or trying to free unallocated memory, but the only way it can avoid dangling pointers is by abolishing free(), or modifying its meaning so it no longer has a use.

David Thornley