home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / lang / cplus / 12302 < prev    next >
Encoding:
Internet Message Format  |  1992-08-12  |  8.9 KB

  1. Path: sparky!uunet!synaptx!synaptics.com!daveg
  2. From: daveg@synaptics.com (Dave Gillespie)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Garbage Collection for C++
  5. Message-ID: <DAVEG.92Aug13025629@synaptx.synaptics.com>
  6. Date: 13 Aug 92 09:56:29 GMT
  7. References: <1992Aug6.014619.2111@ucc.su.OZ.AU>
  8. Sender: daveg@synaptx.Synaptics.Com
  9. Organization: Synaptics, Inc., San Jose, CA
  10. Lines: 177
  11. In-reply-to: mi@starlab.UUCP's message of 10 Aug 92 07:33:22 GMT
  12.  
  13. > Any pointer to a GC class is a GC pointer, not an ordinary pointer.
  14. > GC objects declared on the stack or in other GC classes or static
  15. > are silently converted to GC references (GC pointers with
  16. > reference semantics).
  17.  
  18. I doubt this will be practical.  Partly because it would be too much
  19. of a nuisance to use C++ if you had to keep track of GC vs. non-GC
  20. pointers yourself, and partly because it would be impractical to
  21. convert the libraries (as many people have said).
  22.  
  23. Remember, we're not just talking about the standard C libraries, we're
  24. talking about all the X libraries, all the Unix library functions, all
  25. libraries that any software developer ships to its customers, etc., etc.
  26. It would be a nightmare.
  27.  
  28. There is the argument that "we already did it for `const'", which
  29. is sort-of true, but not really.  Const-ness is not such a pressing
  30. issue:  You can get around a missing `const' in a library function
  31. with a cast (which is "safe" in the practical sense, assuming a
  32. careful and knowledgeable programmer, while casting away `GC' would
  33. not be safe in any sense).  Also, GC pointers would presumably have
  34. some additional cost associated with them (or else why do they
  35. exist?), and users would not enjoy paying this cost for every library
  36. function just *in case* that function was called with a GC pointer.
  37.  
  38. Couldn't we get away with having a garbage collector that didn't
  39. need pointers to be declared `GC'?  The only thing that would need
  40. special `GC' treatment would be "new":  There would be a special
  41. "GCnew" or something which would allocate an object with the
  42. property that it went away when it was no longer referenced.  Note
  43. that it would be safe for an implementation to allocate *all*
  44. objects as GC-able, but it would be a good idea at least in the
  45. transition period to provide both traditional "new" and "GCnew".
  46.  
  47. Some Lisp systems, such as Kyoto Common Lisp, work this way.  Its
  48. garbage collector scans the whole stack as "raw memory," looking
  49. for 32-bit words that could be pointers to GC-able objects.  It
  50. allocates GC-able objects in such a way that it is relatively
  51. easy to tell if a given memory address points to a GC-able object
  52. or not.  For reference, here's KCL's solution (roughly):  Memory
  53. is divided into pages.  All objects stored on a given page will
  54. have the same size.  There is a "page table" which contains, for
  55. each page of memory, the size of the objects stored in that page
  56. (or zero for pages which do not contain GC-able things).  Given
  57. any 32-bit quantity which "might" be a pointer, first take the
  58. high bits to find the page, check if this is a valid GC-able page,
  59. and if so, round the page offset down to a multiple of the page's
  60. object size to get a pointer to an object on the page.  (KCL
  61. actually checks to see if the offset is a multiple of the size,
  62. but since C++ pointers can point into the middle of an object
  63. you'd have to accept any offset as a possibly valid pointer.)
  64.  
  65. KCL scans the stack and other memory where pointers may live; any
  66. word which looks like a pointer to a GC-able object is assumed to
  67. be one.  This means you wind up occasionally keeping around objects
  68. which are garbage, if their addresses happen to be sitting in some
  69. integer variable, say.  But this doesn't hurt anything, and you
  70. never err in the other (dangerous) direction.
  71.  
  72. This may sound slow, but actually I've found KCL's garbage
  73. collector to be quite fast.  KCL's GC is *not* truly generational,
  74. incremental, ephemeral, or what have you---I don't know if their
  75. scheme can survive the transition to these sorts of high-tech
  76. collectors.
  77.  
  78. The vital question is, can a scheme like this be made to work on
  79. all architectures on which C++ could reasonably be expected to be
  80. used?
  81.  
  82. Also, when KCL collects a garbage object it simply puts it on a
  83. free list, it doesn't compact memory.  The actual KCL allocator
  84. does some fancier stuff wherein large, variable-sized things like
  85. strings and arrays go in a separate compactable memory area which
  86. is handled differently, but I don't think you'd be able to get away
  87. with this for C++.  I think you'd have to live with no compaction
  88. at all:  Whereas it is fairly benign to mark an object because
  89. an integer happened to hold its address, you wouldn't want to adjust
  90. that integer when you moved the object!  You'd need special GC
  91. pointers if you wanted compaction, and those would probably not
  92. be practical.
  93.  
  94. On the other hand, C++'s allocator doesn't do any compaction right
  95. now so we wouldn't really be giving anything up.
  96.  
  97.  
  98. If you instead require *classes* to be declared GC, then there is no
  99. point in declaring pointers to be GC as well since a pointer is GC
  100. if, and only if, the pointed-to type is a GC class.  The problem with
  101. this is, suppose you have a GC class which contains a string (i.e.,
  102. a pointer to an old-fashioned non-GC'd array of chars).  Suppose you
  103. no longer have any pointers a certain object of this class, but you
  104. still do have a pointer to its component string.  You lose, if the
  105. destructor for the class deletes the string (which it pretty much has
  106. to do).  So a GC class could only safely contain pointers to other
  107. GC classes, which rules out strings unless you also have GC arrays
  108. of predefined types, which sounds too awkward to work.  Oh, well.
  109.  
  110.  
  111. > GC pointers can be deleted, and the delete is done if and only
  112. > if the pointer is the last one. The pointer has to get nulled out
  113. > by the delete command (so it won't dangle). Alternatively,
  114. > the delete command is just ignored, the delete is applied
  115. > when the pointer goes out of scope, or, when the GC collector
  116. > discovers there are no references to the object.
  117.  
  118. Your first suggestion can only be work if reference counting is
  119. used, or if every delete does most of the work of a global GC!
  120.  
  121. Deleting when the pointer goes out of scope doesn't make much sense.
  122.  
  123. The "when the GC discovers there are no references" option is what
  124. always happens when you have GC, so that's just another way of saying
  125. that "delete" is ignored for GC objects (*not* GC pointers---note
  126. that a GC pointer may point to a non-GC object, no matter what
  127. scheme is being used; otherwise, libraries would not be able to
  128. work both ways).
  129.  
  130. What sounds much more useful is:
  131.  
  132.   GC objects can be deleted, in which case the programmer promises
  133.   that the argument to "delete" was the last existing pointer to the
  134.   deleted object.  The effect is undefined if this is not the case.
  135.  
  136. This allows people to delete things without waiting for a GC to
  137. happen, if they *know* the object is no longer used.
  138.  
  139. I expect the last-existing-pointer rule would be impractically
  140. strict, so how about this:
  141.  
  142.   GC objects can be deleted, in which case any remaining pointers
  143.   to the object become invalid and must not be used to dereference
  144.   an object.
  145.  
  146. In other words, just the same as for deleting objects right now!
  147. You can get dangling pointers from this, but you're not allowed to
  148. use them.  The garbage collector has to be bulletproof enough to
  149. cope with these dangling pointers.  That shouldn't be too hard:
  150. Suppose you do a GC before the deleted memory is reused.  The
  151. deleted memory would be marked, but since it is noted down as
  152. deleted, the GC skips it.  Now suppose you reuse the memory and
  153. then do a GC, with dangling pointers to the "old" use of the memory
  154. still around.  No problem, it's another case of phantom references:
  155. the new object might not get collected as soon as it should, but
  156. nothing truly unsafe happens.
  157.  
  158.  
  159. Here are some more issues that you would have to worry about with
  160. a GC-ing C++:
  161.  
  162. What if a signal handler fires during a GC?  (Maybe you just have
  163. no choice but to have the system mask or disable signals during GC.)
  164.  
  165. What if a GC occurs during a signal handler, and the interrupted
  166. program was in the middle of, say, constructing or destructing an
  167. object?  (I don't think you get bitten either way, but you'd want
  168. to check this *really carefully*!)
  169.  
  170. What if a pointer to a GC-able object is passed to foreign (non-C++)
  171. code, which stashes it somewhere the C++ collector doesn't know to
  172. look?  (All garbage-collecting languages have this problem, and as
  173. far as I know all they can do is shrug and warn programmers not to
  174. let go of the last C++ reference to an object if they know there
  175. might be non-C++ references still lurking around.)
  176.  
  177. What if you have virtual base classes, and there are only references
  178. to the base part of an object?  The derived part of the object has
  179. a pointer to the base part but not the other way around, so the
  180. derived part might be collected in this case.  Can this ever be a
  181. problem?
  182.  
  183.  
  184. Some food for thought,
  185.                                 -- Dave
  186. --
  187. Dave Gillespie
  188.   daveg@synaptics.com, uunet!synaptx!daveg
  189.   or: daveg@csvax.cs.caltech.edu
  190.