none
Garbage Collection - Pros and Limits RRS feed

  • General discussion

  • Been reseraching a bit about the advantages of garbage collection over direct memory management as well as disadvantages limitations lately.

    Thought I write it down for the usual combo of reference for others, as material to link in threads and the check out if my facts hold to the latest results of development.

    Wich is faster, Direct Memory Management or Garbage Collection?

    Direct Memory Management, hands down. A order to free memory can not be slower then a check if the memory is reachable and then an order to free memory. At least for the time of running GC has a clear and unavoidable disadvantage. GC also needs more memory.

    Of course, wich one is faster during execution does not matter quite as much.

    Is Direct Memory Management fast enough for Code/Problem X?

    Yes. If this answer would be no, there would be nothing that IS fast enough. You need faster hardware or a paralell/clustered design to make it work at all. If that is still not fast enough, it is physically or economically impossible.

    Is runtime Garbage Collection fast enough for Code/Problem X?

    99% of the cases, the answer is yes. And frankly it is so much easier to develop with a GC, there is less then no point to not use it in those cases.
    Garbage Collection is just to big a productivity multiplier to ever pass up.

    The other 1% is where it is tricky: The very nature of the GC requires it to pause all other threads during collection. When the GC collects, nothing but the GC runs. Real Time applications and Embedded system fall square into these 1% (but there are solutions for Embedded systems).
    If you got the right scale, the right design and the right GC mode it MAY be fast enough. But don't expect that to hold with future development (or really any development).

    As I often say: GC and Real Time simply do not mix.
    There are strategies to minimise the impact. But once you scale it up enough it will come back to bite you. Realising in time that GC is a dead end for an application can be the most important step in development. People have tried to "make it work" and in the end spend more time working around it then it would have costed them to use direct memory management to begin with. And they still had not a 100% success.
    Theoretical mathematics has not yet found a way around this, so we progammers can not hope to find one either.

    When does the GC collect?

    1. The GC always collects when closing the application.

    If it only runs then, it is the ideal scenario (as it means minimal CPU overhead). This collection might be one of the biggest assets, as the GC never forgets to release resources. One type of memory leak just made impossible. The downside is that the application will not realease any memory until closure (or some other collection happens), so it might be quite a memory hog until it forces itself on a memory diet.

    2. Danger of Out Of Memory Exception.

    Before the Runtime ever throws a OOM exception, the GC has been run to the limit of what it can collect (all generations, every last reference). Two things can cause this scenario: Lack of memory or Fragmentation of memory.
    The thing is this happens a lot more then you realise it: Every time there is a DANGER of a OOM exception, this happens. A lot of times the GC averts that danger, so you don't get a OOM. This will happen a lot without you even noticing it happened. It could happen every single memory allocation if you have enough fragmentation or useage.

    3. Regular Intervall/Concurrent.

    This is usually a two pronged approach:

    First of all, the reachability analysis is done in one (or multiple) paralell background thread. There is no need to halt all threads just to look if something is reachable after all (it get's tricky with weak references, see below).

    Second the actuall collection is called in regular intervalls. It only runs for determined times and collects everything that was "tagged" for it by the analysis threads.

    Basically the idea here is to avoid case 2 runs with regular (shorter) collections. You get shorter pauses, but overall more pause time and a higher CPU load.

    What are the caveeats of case 3 collection?

    In order to save time, this collection usually skips defragmenting the memory space. Wich means that you might still run into a case 2 collection simply due to fragmentation. When that happens it will also be an exceptionally long collection (as the accumulated fragmentation makes it slower).
    Case 3 can stall the case 2 collection. It can not prevent it and will propably make it worse when it happens. There are workarounds - but they do not work infinitely.

    As said before this collection also is bad in dealing with weak references. After all this could happen:
    Tagging thread finds only weak references. Marks object as "collectable".
    Another thread aquires a strong reference via the weak reference.
    GC runs and removes everything marked as "collectable".
    That means the analysis thread cannot properly "work" with weak references. The reachability check has to be done by the collection thread. Wich runs under time constraints, so it might be rather "sloppy" in it's collection and might never get to weak references. Using weak references with Cas 3 collction just made a case 2 collection so much more likely.

    Further caveeats of the GC:

    Even the basic case does not scale infinitely. With 64 bit Hardware our code might run on a computer with up to 4,294,967,295 Giga Byte of Ram (I guess we will get around 4 billion as OS cap in the end). Those new 16 GiB home computers and TB size RAM for servers are nice, but they are like 640 kIB RAM used t be.
    As a side note the current .NET Framework only allows about 8 TiB to be used by 64 bit application (wich is not that much in the big picture).
    You could just split the memory into peices and let one thread do GC on each piece. But that asumes Cores will scale on the same level as memory does. And that it is even feasible to do parallelised, thread save collection (wich I doubt highly).

    Let's take a 3rd Option:

    Obviously GC is a very good idea, but it won't be around forever. It's clear upsides are that they relieve the programmer of the need to free memory (wich he might forget/do twice) and removes all need for the programmer to handle pointers (wich causes a whole host of issues that we best just make into a distant memory).

    The future is propably in "Compile Time Garbage Collection". In additon to looking at runtime and programm closure, the compiler looks wich references can be released at compile time. If you create a instance in a block and the reference can not "escape" the block, it places a direct memory free command at the end of the block. This is especially usefull for massilve paralell operations and any language that prefers to use shortlived helper objects.

    This technique has been tested in the language mercury:
    http://en.wikipedia.org/wiki/Mercury_%28programming_language%29

    One of the first to actually apply it has been Apple. After a short stint with GC for Apps under OS X, they decided to switch fully to ARC instead:
    http://en.wikipedia.org/wiki/Automatic_Reference_Counting

    (They take thier "it must be more respensive then Windows and Linux" very seriously)

    Even found an atricle about Java on the matter, but can not find it again right now.

    So, anything I got wrong or missed?


    Thursday, May 7, 2015 5:12 PM

All replies

  • Christopher,

    Your message reminds me to the far past, when you was told by a guy from a computer company things like:  

    "That part of the competitor is completely unstable. They write about it, but it is on your own risque". 

    A year later we could than read that the company of that guy had a big improvement which made everything better, but it was simply the same the other company already had. 

    The current memory handling in most OS systems is based on preemptive multi-tasking. In my ideas there were better and probably those will come back in future with solved the problems around that, which where 30 years ago easily to solve with preemptive multi-tasking. With that new OS design everything becomes again different.


    Success
    Cor



    Friday, May 8, 2015 9:48 AM
  • "

    (They take thier "it must be more respensive then Windows and Linux" very seriously)

    "

    It's like those silly browser performance tables you see.

    This browser is top browser !

    Crikey, better try it then.

    And there is ( of course ) absolutely NO noticeable performance difference between the two.

    Buy this car here.

    It's got go faster stripes.

    Must be faster.

    Faster is good.

    Buy this car...

    .

    For the vast majority of applications garbage collection is currently a stand out choice because computers are not short of power.

    Developers are, however, fallible.

    Pointers are very easy to f**k up and "slower" but more reliable code beats "faster" but less reliable code every day. Easy choice.


    Hope that helps.

    Technet articles: Uneventful MVVM; All my Technet Articles

    Friday, May 8, 2015 10:25 AM
    Moderator

  • For the vast majority of applications garbage collection is currently a stand out choice because computers are not short of power.

    Developers are, however, fallible.

    Pointers are very easy to f**k up and "slower" but more reliable code beats "faster" but less reliable code every day. Easy choice.

    That is what I am saying too. For 99% of all cases GC is way fast enough. And the benefits just far outstrip any performance impacts it might have for those anyway.

    Identifying the other 1% before you start programming or in time realising that you have a 1% case and switching to direct memory management in time (before you got to much code or design decisions to turn around) - that is an issue.

    Friday, May 8, 2015 12:27 PM
  • The current memory handling in most OS systems is based on preemptive multi-tasking. In my ideas there were better and probably those will come back in future with solved the problems around that, which where 30 years ago easily to solve with preemptive multi-tasking. With that new OS design everything becomes again different.

    Not so sure we can fully return to cooperative multitasking. It could work if we had a lot of cores.

    But in practice the development of preemptive multitasking and now multicre CPU's lead to more and more threads being run.
    You can't even do something like a word processor without at least background threading for open file, save file, print file, the spell check and similar stuff.

    I can count the number of programms on my computer that only use one thread on one hand (there are 5).

    Cooperative would only work somewhat while Number of Cooperative Only Threads < Real CPU cores. And I don't see developers of programms being so cooperative to keep it that low.


    Friday, May 8, 2015 12:46 PM
  • "

    Identifying the other 1% before you start programming or in time realising that you have a 1% case and switching to direct memory management in time (before you got to much code or design decisions to turn around) - that is an issue.

    "

    I take your point... however.

    I think the percentage of applications vary depending on what sort of field you are in.

    The stuff I do, it's tending to 0% need that.

    In say game graphics, maybe that'd be higher.

    I think it's often fairly obvious that a process will be very processing intensive.


    Hope that helps.

    Technet articles: Uneventful MVVM; All my Technet Articles

    Friday, May 8, 2015 2:04 PM
    Moderator