It seems like people got tired of manual memory management, so they invented garbage collection, and life was reasonably good. But what about every other resource types? File descriptors, sockets, or even user created data like database connections?

This feels like a naive question but I cannot find any place where anyone has asked it. Let's consider file descriptors. Say a program knows that it will only be allowed to have 4000 fds available when it starts. Whenever it performs an operation that will open a file descriptor, what if it would

  1. Check to make sure that it isn't about to run out.
  2. If it is, trigger the garbage collector, which will free a bunch of memory.
  3. If some of the memory freed held references to file descriptors, close them immediately. It knows the memory belonged to a resource because the memory tied to that resource was registered into a 'file descriptor registry', for lack of a better term, when it was first opened.
  4. Open a new file descriptor, copy it into new memory, register that memory location into the 'file descriptor registry' and return it to the user.

So the resource would not be freed promptly, but it would be freed whenever the gc ran which includes at the very least, right before the resource was about to run out, assuming it isn't being entirely utilized.

And it seems like that would be sufficient for many user defined resource cleanup issues. I managed to find a single comment here that references doing cleanup similar to this in C++ with a thread that contains a reference to a resource and cleans it up when only it has a single reference remaining (from the cleanup thread), but I can't find any evidence of this being a library or part of any existing language.

up vote 3 down vote accepted

GC deals with a predictable and reserved resource. The VM has total control over it and has total control over what is created and when. The key words here are "reserved" and "total control". On the other hand, handles and are resources allocated by the OS, and pointers are... well pointers to resources allocated outside the managed space. Because of that, handles and pointers are not restricted to be used inside managed code. Handles and pointers can be used - and ofter are - by managed and unmanaged code running in the same process. A managed code can invoke native methods to pass or receive handles and pointers. A "Resource Collector" can not be sure that a handle is not being used just by checking if a managed resource holds a reference to a handle or pointer.

A practical example is the .NET CLR. One can use flavored C++ to write code which works with both managed and unmanaged memory space; handles, pointers and references, all in the same scope, the perfect storm.

Thinking about it, it should be very easy to write a Resource Collector that only acts in the last moment, when there is no more room to the program to do its work. But it's clearly a very bad design; it's not even close to be efficient, and it probably would lead to unpredictable behavior caused by resource starvation. Too many downsides...

This appears to be one of the reasons languages with garbage collectors implements finalizers. Finalizers are intended to allow a programmer to clean up an object's resources during garbage collection. The big problem with finalizers is that they aren't guaranteed to run.

There's a pretty good write-up on using finalizers here:

Object finalization and cleanup

In fact it specifically uses the file descriptor as an example. You should make sure to clean up such resource yourself, but there is a mechanism in place that MAY restore resources that weren't properly released.

  • I'm not sure if this answers my question. It is missing the part of my proposal where the system knows that it is about to run out of a resource. The only way to hammer that part in is to ensure that you manually run the gc before you allocate new file descriptors, but that is extremely inefficient, and I don't know if you can even cause the gc to run in java. – mindreader Feb 6 '16 at 19:55
  • OK, but file descriptors usually represent an Open File in the operating system which implies (depending upon the OS) use system level resources such as locks, buffer pools, structure pools, etc. . Frankly, I don't see the benefit of leaving these structures open for a later garbage collection and I see many detriments to leaving them allocated longer than necessary. The Finalize() methods are intended to allow a last ditch cleanup in the event that a programmer overlooked calls to clean up resources, but shouldn't be relied upon. – Brian Hibbert Feb 7 '16 at 13:25
  • My understanding is that the reason they shouldn't be relied upon is that if you were to allocate a ton of these resources, like maybe you are descending down a file hierarchy opening each file, you may open too many files before the gc happens to run, causing a blowup. The same thing would happen with memory, except that the runtime checks to make sure it will not run out of memory. I would like to know why a system cannot be implemented to reclaim arbitrary resources before the blowup, in nearly the exact same way as memory is done. – mindreader Feb 8 '16 at 14:05
  • A system COULD be written to GC resources other than memory, but you would have to track reference counts or have some other method of determining when a resource is no longer in use. You DON'T want to deallocate and reallocate resources that are still in use. All manor of mayhem can ensue if a thread has a file open for write, the OS "reclaims" the file handle and another thread opens a different file for write using the same handle. And I'd also still suggest that it's a waste of significant resources to leave them open until a GC like thread gets around to releasing them. – Brian Hibbert Feb 8 '16 at 14:31

There are many programming techniques to help manage these kinds of resources.

  • C++ programmers often use a pattern called Resource Acquisition is Initialization, or RAII for short. This pattern ensures that when an object that holds onto resources goes out of scope, it will close the resources it was holding on to. This is helpful when the object's lifetime corresponds to a particular scope in the program (e.g., when it matches the time when a particular stack frame is present on the stack), so it is helpful for objects that are pointed to by local variables (pointer variables stored on the stack), but not so helpful for objects that are pointed to by pointers stored on the heap.

  • Java, C#, and many other languages provide a way to specify a method that will be invoked when an object is no longer live and about to be collected by the garbage collector. See, e.g., finalizers, dispose(), and others. The idea is that the programmer can implement such a method so that it will explicitly close the resource before the object is freed by the garbage collector. However, these approaches do have some issues, which you can read about elsewhere; for instance, the garbage collector might not collect the object until far later than you'd like.

  • C# and other languages provide a using keyword that helps ensure that resources are closed after they're no longer needed (so you don't forget to close the file descriptor or other resource). This is often better than relying on the garbage collector to discover that the object is no longer live. See, e.g., https://stackoverflow.com/q/75401/781723. The general term here is a managed resource. This notion builds on RAII and finalizers, improving on them in some ways.

  • I am less interested in prompt resource deallocation, and more interested in the idea of just in time deallocation. RIAA is great, but not super applicable to very many garbage collection languages. Java is missing the ability to know when it is about to run out of a certain resource. Using and bracket type operations are useful and deal with errors, but I'm not interested in them. I simply want to allocate resources and then they will clean themselves up whenever it is convenient or necessary, and there's little way to screw it up. I guess no one has really looked into this. – mindreader Feb 6 '16 at 20:01

All memory is equal, if I ask for 1K, I don’t care where in the address space the 1K comes from.

When I ask for a file handle, I want a handle to the file I wish to open. Having a file handle open on a file, often blocks access to the file by other processes, or machine.

Therefore file handles must be closed as soon as they are not needed, otherwise they block other accesses to the file, but memory only needs to be reclaimed when you start to run out of it.

Running a GC pass is costly and is only done “when needed”, it is not possible to predict when anther process will need a file handle that your process may no longer be using, but still has open.

  • Your answer hits the real key: memory is fungible, and most systems have enough that it doesn't need to be reclaimed especially quickly. By contrast, if a program acquires exclusive access to a file, that will block any other programs everywhere in the universe that might need to use that file, no matter how many other files may exist. – supercat Jan 15 '17 at 3:17

I would guess the reason why this has not been approached much for other resources is exactly because most other resources are preferred to be released as soon as possible for anyone to reuse.

Note, of course, your example could be provided now using "weak" file descriptors with existing GC techniques.

To check if memory is not longer accessible (and thus guaranteed not to be used anymore) is rather easy. Most other types of resources can be handled by more or less the same techniques (i.e., resource acquisition is initialization, RAII, and its counterpart of freeing when the user is destroyed, which links it with memory administration). Doing some kind of "just in time" freeing is impossible in general (check the halting problem, you would have to find out that some resource was used for the last time). Yes, sometimes it can be done automatically, but it is a much messier case as memory. So it relies on user intervention for the most part.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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