2

Note: I use term "resource" in opposition to memory here, resource here is file, socket, etc.

Is there a language implementation that uses M&S GC (but not necessarily only that kind ¹) that is able to automatically free the resources (like file handles)? If yes, how it is done?

By automatically I mean that user can mark in some way the resources (not their usage though), but does not have to remember all the time to mark the actual act of freeing it. For example I consider C# with its using, F# with its use, OCaml with protect/finally pattern as manually freeing resources.

In other words resources should be handled 100% deterministically, once they are no longer used they should be freed right away.

So far I didn't notice a language implementation with M&S GC capable of doing it.

¹ I made that remark because maybe some hybrid approach could provide best of the worlds -- RC for resources, M&S for the rest.

  • How is "marking" a resource for immediate cleanup any different from a using block? – Becuzz Feb 20 '18 at 14:58
  • 1
    You might be interested in Rust's ownership system. – doubleYou Feb 20 '18 at 15:05
  • @Becuzz, I meant marking resource (for example FileWriter would be marked once as resource type), not marking its usage. – greenoldman Feb 20 '18 at 15:05
  • 1
    Closing a file is different from freeing the used memory. A using block handles the former; the garbage collector handles the latter when it is convenient to do so. – Robert Harvey Feb 20 '18 at 15:48
  • 1
    What do you mean by "a language that uses mark&sweep"? As far as I know, there is not a single language that does so. There are certainly language implementations that use mark&sweep (although they are getting fewer and fewer also in favor of more sophisticated algorithms such as G1, Monotone, etc.) but no language I know of uses mark&sweep. – Jörg W Mittag Feb 20 '18 at 17:00
10

“100% deterministically, once they are no longer used they should be freed right away” – that is incompatible with garbage collection. But it's not necessarily at odds with automatic resource management.

If resources are to be freed immediately, this means that GC must run immediately each time when a reference is released. One such strategy is reference counting. Refcounting is a valid resource management approach, but has some drawbacks:

  • Each managed object needs an extra field to store its reference count.
  • The refcount needs to be modified each time a reference is acquired or released.
  • The reference count needs to be modified atomically, if the object might be shared across threads.
  • Cyclic references cannot be released, and require a full garbage collection algorithm instead.

Sometimes it can be determined at compile time where a resource must be released. This is the basis of automatic variables in C++, and the ownership system in Rust. The C++ variant is of course unsound because resource might be released while it is still referenced. But because these systems work at compile time rather than runtime, it is not sensible to compare them to GC.

An important property of these systems is that there's an explicit concept of ownership. A resource is bound to a particular variable. It can't be copied to another variable. It may only be moved permanently, or temporarily borrowed. This is completely unlike objects in a garbage-collected system where you can freely copy object references around, and ownership is shared among all references.

So it is not generally possible to combine automatic-deterministic resource management with garbage collection. GC is primarily concerned about the “memory” resource, which is commonly used in many programs. Memory is fungible: those unused bytes over there are just as good as the bytes occupied by this maybe-dead object. It is much faster to simply use known-free memory than to free memory immediately. Other resources do not have this property, e.g. sockets should be closed in a timely manner. So if a language implies GC, this is a tradeoff: memory management is made safe and fast, at the cost of having to manage non-fungible resources manually.

If a language were to support GC and RAII-style resources, these resources cannot be subject to GC. So the language would have to support two distinct kinds of objects. This greatly complicates the language as these two kinds of objects must be treated statically as different and unexchangeable types. This wouldn't necessarily have to be special syntax like resource Foo f = acquireResource(); ..., but given the drastically incompatible semantics such syntax would be sensible. Incidentally, using (Foo f = acquireResource()) { ... } in C# happens to be exactly such syntax and static analysis can warn you if you acquire a resource without making sure it's cleaned up. This somewhat sidesteps the RAII vs. GC resource management incompatibilities as the resource will be released before the object representing the resource is eligible to be freed. The drawback is that this isn't entirely sound: you can reference a resource that was already released.

  • Absolutely fantastic answer, thank you very much! And I honestly mean it. – greenoldman Feb 20 '18 at 16:04
  • 3
    "this means that GC must run immediately each time when a reference is released. This happens to be equivalent with reference counting" This is certainly the pattern used in Refcounting, but it's not equivalent to refcounting. Pure refcounting for instance can't handle a cyclic dependencies, whereas dependence graph GCs have no issue with those; And you certainly don't need to be refcounting to trigger a full GC run every time a resource could potentially be released (although doing this would obviously be inadvisable) – Cubic Feb 20 '18 at 16:04
  • 1
    @Cubic I updated that sentence. But yes, If I want 100% deterministic resource management by using any GC strategy (incl. refcounting), then I will have to launch a full GC cycle to prove the liveness of a resource in each situation where the resource should perhaps be closed. That's of course undesirable, so most refcounting languages are only deterministic as long as the programmer manually guarantees acyclic object graphs. – amon Feb 20 '18 at 16:07
  • With regard to reference counting, Swift is perhaps the language to look at for inspiration, being the only popular modern language I can think of that uses it. The documentation here gives a good description of the consequences. – Jules Feb 21 '18 at 9:07
  • Yes, Swift needs to use refcounting in order to have compatible semantics with Objective-C. Other common languages that use refcounting: CPython (as part of the GC algorithm), C++ (with std::shared_ptr<>), and Perl. – amon Feb 21 '18 at 10:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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