3

By "Automatic Reference Counting", I am refering to the specific feature of the Clang compiler. By "Garbage Collection", I am refering to Tracing Garbage Collection, not to "Garbage Collection" in general.

The main difference as I understand is that ARC gives you control over making optimizations if needed, while in a garbage collected system the software engineer doesn’t need (?) to consider memory management. Since it appears that ARC strikes more of a balance of control and abstraction, are there environments outside of Objective-C/Swift that implement something like this? Are there weaknesses to this model of memory management that I’m missing?

7
  • 2
    Is there something particular you're interested to compare? See What is the problem with "Pros and Cons"?. But to answer some sub-questions: CPython also uses ARC, although it has a GC as well (that they call the "cycle collector") to solve the cycle problem instead of weak refs. Since ARC does most of the memory management, their GC needs to run less often than a GC-only system. Rust also has std::rc::Rc and std::arc::Arc, and C++ has std::shared_ptr
    – Alexander
    Apr 24, 2022 at 2:15
  • 1
    @DocBrown, surely it would be fairer to the OP to challenge his framing than to penalise his question? See the comment on my answer - I too have never before heard of ref-counting being a form of garbage collection, nor that garbage collection meant any strategy of releasing memory (and not specifically the tracing-style release).
    – Steve
    Apr 24, 2022 at 10:53
  • 1
    @Steve: and GC does not mean "any strategy of releasing memory". It means stratregies of automatically releasing memory by detecting which pieces of memory are still in use and which not.
    – Doc Brown
    Apr 24, 2022 at 11:17
  • @DocBrown, I'll reply to this now on the comments on my answer.
    – Steve
    Apr 24, 2022 at 11:30
  • 4
    The main weakness is that reference counting does not detect cycles. If you have two objects with references to each other, they are not collected by reference-counting GC, even if there are no live references to either. On the other hand reference counting is deterministic which is an advantage - you know an object will be collected when the reference goes out of scope rather than at some indeterminate time in the future. Python uses (or used?) a combination where reference counting was backed by GC to collect orphaned cycles.
    – JacquesB
    Apr 24, 2022 at 11:39

4 Answers 4

2

One difference, already mentioned in another answer, is that ARC promptly frees objects that are no longer needed, whereas GC may only free objects in response to pressure from the heap allocator. That becomes an important distinction if the language offers some kind of a "destructor" method that is called when an object gets reclaimed.

When Java was young, it gave us the absolutely abhorrent finalize() method which, thank Goodness, has finally been deprecated. The finalize() method would be called in the GC thread, just before your object was reclaimed, and it was supposed to be where you could do things like close file handles, etc. The problem with that was, Java could not guarantee that finalize() would ever be called, and if it was called, it would be because of pressure from the memory allocator, and not because the system was running short of file handles (or, whatever other non-memory resource.)

Python, on the other hand, has the __del__ method, which will be immediately called when your object becomes unreachable (so long as you are careful not to create circular references in your data structures.) That makes it perfectly reasonable to use __del__ for closing files.*


* In those cases where using a context manager would not be a smarter choice.

0
0

My understanding of the tradeoff has always been that GC systems have some overheads compared to RefCount systems.

The overheads are twofold. Firstly, GC systems are harder to design in the first place, and require more software infrastructure to support the application code. Secondly, there is a penalty at runtime in terms of operating the garbage collection process (a penalty that on slower hardware can cause hesitation noticeable to human users).

But set against the GC overhead is the fact that the programmer is released almost entirely from the faff of memory management, especially those caused by programming patterns and features that necessitate a very complicated process of memory management (and which potentially would cause a very significant design and runtime overhead in themselves, when implemented without GC).

It's also important not to overstate the overheads of GC. Almost every aspect of computing is characterised by bearing significant computational overheads that make it easier to program and use a computer for its intended application.

There still remain cases where the overheads of GC cannot be tolerated. Algorithms or portions of code that are intense relative to the capability of the hardware, and applications that require predictable real-time response to input, are examples.

But my impression over recent years is that the number of problems incompatible with GC only ever shrinks.

13
  • 1
    @DocBrown, I have to admit that I'd never before encountered the idea that ref-counting was a form of garbage collection, or that garbage collection wasn't a synonym for a "tracing-style" garbage collection. Particularly because I understood the point of ref-counting was that "garbage" never exists - the memory is either in use, or the last user releases it before abandoning the pointer. If garbage exists in a ref-count system, it is only in the form of leaked memory, which is never collected.
    – Steve
    Apr 24, 2022 at 10:47
  • @DocBrown, I can't disagree with your criteria, I've simply never encountered this conception before. Regarding Wikipedia, the main article on GC here seems very much to start from the idea that tracing-style GC is the only kind. Only later does it say ref-counting is also a GC strategy. I think there's quite possibly a lack of clarity on this point, or competing terminology. I should also declare that my detailed understanding of ref-counting is entirely from Microsoft COM. Is COM an example of a GC system in your view?
    – Steve
    Apr 24, 2022 at 11:29
  • 2
    "My understanding of the tradeoff has always been that GC systems have some overheads compared to RefCount systems." – Reference Counting also has overheads, namely the updating and storing of the Reference Counts. This leads to schemes such as Deferred Reference Counting where instead of updating the Reference Count immediately, the operations get placed in a queue and processed by a background thread. Or Optimized Deferred Reference Counting where multiple Reference Count updates for the same object get collapsed into one update (or even none, if they happen to cancel out). Apr 25, 2022 at 6:17
  • 2
    Then there is the problem that writes are really bad for performance, but updating the Reference Count always implies a write, even for purely functional operations on deeply immutable objects. Also, the naïve implementation of putting the Reference Count inside the object header suffers from poor cache locality, leading to schemes where the Reference Counts are kept in a separate side-table. Lastly, deallocating one object can trigger a cascade of deallocations which leads to long pause times. There are ways to deal with most of these problems (see for example Blackburn and McKinley's … Apr 25, 2022 at 6:20
  • 1
    Ulterior Reference Counting), but then your claim that Reference Counting is simple and requires less infrastructure than tracing GC no longer holds. In fact, simple tracing GCs are very simple and real-world high-performance Reference Counting implementations are very complex, so I do not see much of a difference there. Apr 25, 2022 at 6:22
-1

Book Keeping

When and Who is the question.

Reference Counting is done inline with code and is only slightly less intrusive than explicit memory management. On the pro side you get nearly instant memory de-allocation, on the con side the book keeping is done inline and not necessarily when your program wants to be de-allocating expensive data-structures. Also there is the caveat that the object graph must be a DAG, unless the reference counting is paired with a periodically run graph tracer for at least some key classes.

Garbage collection is done out of line, generally when allocating memory and realising that there isn't enough to satisfy the request, or on a background thread that kicks in when the program is taking a "breather". In the first case it can significantly slow down an algorithm sporadically, in the second your program actually needs breathers in which to reclaim resources. There isn't a guarantee that the objects will be reclaimed. The boon is that memory isn't a worry. The con is that programmers are often tricked into thinking this includes non-memory resources like file handles and ports.

There is another way of handling memory/resources. Quiescent States. Essentially de-allocate the objects when we arrive at some pre-determined checkpoints where both memory contention is low, and time is available. It does mean that a programmer has to think about when to do the book-keeping, and there is the chance of running out of memory/resources. But on the plus side it can be done at a time when your program is least affected by the work involved.

The book keeping has to happen - unless your program is so short lived it can do what it needs to do and die before running out of heap/resources - in which case the OS is doing the book-keeping when it cleans up the dead process.

2
  • This question is explicitly about automatic reference counting a la Swift or Python, not explicit reference counting as in old-school Objective-C.
    – gntskn
    Apr 26, 2022 at 12:11
  • Reference counting is still reference counting no matter if it is built in to the compiler, or a user kludge. You the programmer must still be aware of not allowing cycles, and if you do, make sure there are cycle detection algorithms happening to cleanup.
    – Kain0_0
    Apr 27, 2022 at 2:07
-5

You have no choice. You can’t run Java with ARC, you can’t run Swift with garbage collection. Just learn how to use what you have.

1
  • I'm not the ones who downvoted your answer, but the OP's question is asked from the perspective if you were the designer of green-field Java or if you were the designer of green-field Swift from scratch at version 0.0, which one would you choose & why versus which one would you reject & why and does there exist any improvement/hybrid between the 2 that mitigates their tradeoffs/downsides? In practice with existing settled-state technology, your answer does have much wisdom though. Apr 24, 2022 at 16:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.