The obvious cases for not using garbage collection are hard realtime, severely limited memory, and wanting to do bit twiddling with pointers. Are there any other, less discussed, good reasons why someone would prefer manual memory management instead of GC?
相关问题
- What uses more memory in c++? An 2 ints or 2 funct
- C# GC not freeing memory [duplicate]
- Achieving the equivalent of a variable-length (loc
- garbage collection best practices
- Too many Garbage collection threads
相关文章
- Should client-server code be written in one “proje
- Algorithm for maximizing coverage of rectangular a
- Is there an existing solution for these particular
- -fno-objc-arc not working to disable ARC
- How would a heap-allocated const object differ fro
- What is Scope Creep? [closed]
- Why do we need Dispose() method on some object? W
- On scala project - Getting error GC overhead limit
Temporary insanity?
Actually the only case I know of that you haven't covered is so-called "lifetime-based memory management", which sometimes goes under the name "pools" or "arenas" or "regions". The idea is that you're going to allocate a ton of objects, probably small, and they're all going to die at once. So you have a cheap allocator and then you recover all the objects in a single free operation.
These days there are program analyses that will enable a compiler to do this for you, but if you're writing C code, you do it by hand. There's an implementation with examples in Dave Hanson's C Interfaces and Implementations, and it's used in Fraser and Hanson's
lcc
compiler, which is written in C without a garbage collector.The only reason NOT to use Garbage Collection for resource management is if you want to use RAII ala C++, but as it applies purely to memory, even then it's a reasonable idea to use it. (Note: It's still possible to do so, with gotcha's related to non-deterministic finalization).
That said, using Garbage Collection can use more memory than is strictly needed, so in severely memory constrained areas where one can not even spare the memory for managing the GC routines (and the code), then that's a good reason not to use it, too.
Additionally, if the language you use doesn't contain GC by default, such as C++, and you like to use RAII, then that's a reasonable reason too, though for some problems it can be very useful.
Ultimately it comes to tradeoffs - the more specialized your requirements, especially with regards to thread-safe RAII, the more complex it is to implement the GC, and GC might not buy you very much for your application.
If you have a ton of objects which are freed rarely the garbage collector will start and waste time just to find out that there are only a few objects to finalize. In extreme cases this may cause a huge performance penalty.
How about for security reasons? Eg if you've got an encryption private key in memory, you'd probably want it around for the shortest possible time.
Having said that, i think the way hardware is heading, learning the art of multithreaded programming may be more worth learning.
When you are building high-performance apps such as first person shooter games, you don't want GC that will potentially impact your app's execution. Manually managing the memories in those apps allow you to decide the right time to free up resources.
Perhaps the most important unspoken issue is the code that the VM injects in order to make it work in harmony with the GC. In particular, all production-quality GCs silently incur a write barrier whenever a pointer is written into the heap.
For example, the following F# program creates an array of 10,000 ints and then exchanges them:
Change that
int
tostring
and the program runs 2x slower because ints can be exchanged directly whereas exchanging reference types must incur two write barriers.Another important situation that people love to brush under the rug is pathological behaviour of conventional GCs. Today, most GCs are generational which means they bump allocate into a nursery and then evacuate survivors to an older generation (typically mark-sweep). This works well when the generational hypothesis (that objects die young and old objects rarely refer to newer objects) holds because most of the objects in the nursery are dead when it is efficiently swept. But objects don't always die young and old objects are sometimes full of pointers to new objects.
In particular, the pathological behaviour of a generational GC is manifested when a program allocates a large array-based mutable data structure (e.g. hash table, hash set, stack, queue or heap) and then fills it with freshly-allocated objects. These new objects survive because they are referred to from an older object, completely violating the generational hypothesis. Consequently, solutions using GCs are typically 3x slower than necessary here.
FWIW, I believe mark-region GCs have the potential to evade these problems in the future. In a mark-region GC, the old generation is a collection of former nurseries. When a thread-local region fills and is found to contain mostly reachable objects the whole region can be logically migrated into the old generation without copying any objects and a new nursery can be allocated (or an non-full old nursery can be recycled).