tom@ssd.csd.harris.com (Tom Horsley) (05/15/91)
In a generation scavenging garbage collector (or I suppose any other form of
copying collector) you never explicitly "delete" an object in memory, you
just fail to copy it to the new memory area.
With a garbage collector that works like this, how do you go about
implementing "destructor" calls (to use C++ terminology). Just as one
example of why you might want a destructor: you could have a stream I/O
object connected to some low level operating system file descriptor, when
the I/O object is no longer referenced, you would like to close the file
descriptor.
I have read quite a few papers on garbage collection, but I don't recall
ever seeing this issue addressed. I have though of a few ways to implement
destructors, but I am wondering if any existing systems have clever
algorithms to solve this problem. Does anyone know of any papers that
describe good ways to do this, or any existing systems that do this?
--
======================================================================
domain: tahorsley@csd.harris.com USMail: Tom Horsley
uucp: ...!uunet!hcx1!tahorsley 511 Kingbird Circle
Delray Beach, FL 33444
+==== Censorship is the only form of Obscenity ======================+
| (Wait, I forgot government tobacco subsidies...) |
+====================================================================+
kend@data.UUCP (Ken Dickey) (05/16/91)
tom@ssd.csd.harris.com (Tom Horsley) writes: >In a generation scavenging garbage collector (or I suppose any other form of >copying collector) you never explicitly "delete" an object in memory, you >just fail to copy it to the new memory area. >With a garbage collector that works like this, how do you go about >implementing "destructor" calls (to use C++ terminology). Closing ports (files) and making dumb storage allocators clean up after themselves is done either by implementing a finalization service (i.e. registering objects which need finalization) or by using an implementation which does this for you. The basic strategy is to have a "post-gc-deamon" which gets called just after a collection occurs and which checks for (and "finalizes") any newly dead objects. A general strategy is to use "weak" pointers and then check after gc for the broken ones (the ones which did not survive). There are a number of Lisp collectors which implement weak pointers, weak pairs, populations, etc. {E.g. look at the T implementation from YALE}. One reference for weak pointers (copying collector implementation) is in Jim Miller's PhD thesis: "MultiScheme", MIT/LCS/TR-402, MIT, September 1987. -Ken Dickey kend@data.uucp
dlw@odi.com (Dan Weinreb) (05/20/91)
I worked for about ten years developing and maintaining system software in Lisp, in which all deallocation was done automatically by a garbage collector. Objects had the equivalent of C++ constructors (called the ":init" method), but there was no equivalent of C++ destructors. (We didn't make any significant use of the "weak link", "post-gc-action" sort of thing that was mentioned in an earlier reply to your posting.) Generally, I don't think it would have been terribly useful to have a feature that would run a method when an object was collected by the GC, for several reasons: (1) If the object contained references to other objects, those other objects could be garbage too, and might no longer exist. So such a method would be exceedingly unsafe. (2) There's no telling when the GC will actually run. It might not be for hours, in principle. So you'd never know when the action would happen. Nowadays I program in C++, so I can compare the techiques used in the two languages. Consider an automatic object in C++. The destructor is run when the block is exited, either normally or because an exception unwinds the block. (Yes, C++ exceptions aren't in the language yet, but they will be.) In Lisp, there is a construct called unwind-protect that lets you set up the same kind of control structure. Those actions that might be placed in a C++ destructor would often correspond, in Lisp, to actions in the handler part of an unwind-protect. An unwind-protect handler might send a message to an object (a.k.a. call a generic function on an object). Sometimes the unwind-protect with this handler would be packaged up in a Lisp macro. A typical such macro, part of the built-in system, is "with-stream", which opens a stream, executes a body, and then (in the unwind-protect handler) closes the stream. For heap-allocated objects, there isn't anything in Lisp that corresponds to the C++ destructor. Instead, where a C++ programmer would use a "delete" statement, the Lisp programmer would just send some message (a.k.a. call some generic function) that would perform actions analogous to those in the C++ destructor.