ObjectMemory.st
changeset 403 e4d9cc32c794
parent 384 cc3d110ea879
child 418 d49a94b08368
--- a/ObjectMemory.st	Fri Aug 18 01:52:44 1995 +0200
+++ b/ObjectMemory.st	Sat Aug 19 03:33:23 1995 +0200
@@ -34,7 +34,7 @@
 COPYRIGHT (c) 1992 by Claus Gittinger
 	     All Rights Reserved
 
-$Header: /cvs/stx/stx/libbasic/ObjectMemory.st,v 1.51 1995-08-11 03:02:19 claus Exp $
+$Header: /cvs/stx/stx/libbasic/ObjectMemory.st,v 1.52 1995-08-19 01:33:13 claus Exp $
 '!
 
 !ObjectMemory class methodsFor:'documentation'!
@@ -55,25 +55,31 @@
 
 version
 "
-$Header: /cvs/stx/stx/libbasic/ObjectMemory.st,v 1.51 1995-08-11 03:02:19 claus Exp $
+$Header: /cvs/stx/stx/libbasic/ObjectMemory.st,v 1.52 1995-08-19 01:33:13 claus Exp $
 "
 !
 
 documentation
 "
-    This class contains access methods to the system memory -
-    in previous versions this stuff used to be in the Smalltalk class.
-    It has been separated for better overall structure.
+    This class contains access methods to the system memory and the VM.
+
+    In previous ST/X versions, this stuff used to be in the Smalltalk class.
+    It has been separated for better overall class structure and modularisation.
     There are no instances of ObjectMemory - all is done in class methods.
-
-    Many methods here are for debuging purposes only, and not standard.
+    (this is a functional interface).
+
+    Many methods here are for debuging purposes, for developers
+    or experimental, and therefore not standard.
     Do not depend on them being there - some may vanish ...
     (especially those, that depend on a specific GC implementation)
-
-    Warning:
-      The InterruptHandler variables are known by the runtime system -
-      they are the objects that get an interrupt message when the event
-      occurs. You may not remove them.
+    Most of the stuff found here is not available, or different or called
+    different in other smalltalk implementations. Be aware, that using these
+    interfaces (especially: depending on them) may make your application
+    non portable.
+
+    See more documentation in -> caching
+			      -> interrupts
+			      -> garbageCollection
 
     Class variables:
 
@@ -129,6 +135,11 @@
 	BackgroundCollectProcess        created by startBackgroundCollectorAt:
 
 	BackgroundFinalizationProcess   created by startBackgroundFinalizationAt:
+
+    Warning:
+      The InterruptHandler variables are known by the runtime system -
+      they are the objects that get an interrupt message when the event
+      occurs. You may not remove any of them.
 "
 !
 
@@ -177,8 +188,11 @@
 
     Another reason for having handler objects is that they allow interrupt handling
     without any context switch, for high speed interrupt response.
-    However, special care is needed, since it is not defined, which process gets
-    the interrupt and will do the processing.
+    However, if you do this, special care is needed, since it is not defined, 
+    which process gets the interrupt and will do the processing (therefore,
+    the default setup installs handlers which simply signal a semaphore and
+    continue the running process).
+
     Typically, the handlers are set during early initialization of the system
     by sending 'ObjectMemory XXXInterruptHandler:aHandler' and not changed later.
     (see Smalltalk>>initialize or ProcessorScheduler>>initialize).
@@ -201,33 +215,86 @@
 	fpExceptionInterrupt            - floating point exception (SIGFPE)
 	childSignalInterrupt            - death of a child process (SIGCHILD)
 	signalInterrupt:<number>        - unix signal (if other than above signals)
+
+    To avoid frustration in case of badly set handlers, these messages
+    are also implemented in the Object class - thus anything can be defined
+    as interrupt handler. However, the VM will not send any
+    interrupt message, if the corresonding handler object is nil
+    (which means that nil is a bad choice, if you are interrested in the event).
+
+    Interrupt processing is not immediately after the event arrives: there
+    are certain ``save-places'' at which this handling is performed
+    (message send, method return and loop-heads).
+    If not explicitely enabled, primitive code is never interrupted.
+
+    Interrupts may be disabled (OperatingSystem blockInterrupts) and reenabled
+    (unblockInterrupts) to allow for critical data to be manipulated.
+    Every process has its own interrupt-enable state which is switched
+    when processes switch control (i.e. you cannot block interrupts across
+    a suspend, delay etc.). However, the state will be restored after a resume.
 "
 !
 
 garbageCollection
 "
-    Currently, Smalltalk/X uses a two-level memory hierachy.
+    Currently, Smalltalk/X uses a two-level memory hierachy (actually, there
+    are more memory regions used for stack, permanent objects, symbols etc.
+    but for the following discussion, these are not of interrest).
+
+  newSpace:
+
     Objects are created in a so-called newSpace, which is relatively small.
-    This newSpace is cleaned by a scavenge-operation, whenever becoming
-    full. Scavenging means, that all still-live objects (i.e. referenced by some 
-    other) are copied over to another memory area, leaving all unreferenced
-    objects as garbage behind. After this copying, these two semispaces exchange their
-    roles - i.e. objects are copied ping-pong like between these semispaces.
+    This newSpace is cleaned by a so called ``scavenge''-operation, whenever 
+    becoming full. Scavenging means, that all still-live objects (i.e. referenced
+    by some other) are copied over to another memory area, leaving all unreachable
+    objects as garbage behind. Thus, the newSpace actually consists of two semispaces,
+    of whih only one is active - the other being used only while objects are
+    copied.
+    After this copying, these two semispaces exchange their roles - i.e. reachable
+    objects are copied ping-pong like between these semispaces.
     Once an object survives enough of these copying operations, the next scavenge 
     will move it into the so called oldSpace, which is much larger, and not
     processed by the scavenger. 
-    This movement of an object from newSpace to oldSpace is called 'tenure'.
+    This movement of an object from newSpace to oldSpace is called ``tenure''.
+    (this avoids objects being copied around forever).
+    Once tenured, an object is no longer contained in the newSpace, and
+    thus ceases to create any scavenging overhead after that.
 
     Scavenging occurs automatically, and is usually done fast enough to go 
     unnoticed (typically, it takes some 5 to 50ms to perform a scavenge, 
     depending on how many live objects are in the newspace).
+
     Interrestingly, the scavenger performs better, if many garbage objects
     are to be reclaimed, since less object-copying has to be done. Therefore,
     the best-case scavenge time is almost zero, if there is only garbage in
     the newSpace. In contrast, the worst-case is when all newSpace objects are still
-    living. To honor this situation, the system uses an adaptive tenure-count,
+    living. Thus, from a newSpace collectors viewPoint, it makes sense to get
+    objects out of the way as fast as possible. However the oldSpace is
+    collected much less frequently and the cost to reclaim an oldspace object
+    is much higher (actually, the cost to reclaim a newspace object is zero -
+    its the survival of objects which we have to pay for).
+    Therefore, from an oldSpace collectors point of view, its preferable to
+    keep objects in the newSpace as long as possible.
+
+    To honor this conflicting situation, the system uses an adaptive tenure-count,
     which adjusts the number of scavenges required for tenure (the so called 
     'tenureAge') according to the fill-grade of the newSpace.
+    If the newSpace is relatively empty, it tries to keep objects longer there.
+    The controlling parameters of the tenure age can be changed dynamically,
+    detailed information is available upon request.
+
+    The exact speed of the scavenger depends mostly on the speed of your memory
+    interface (and, since most of todays memories have access times in the order
+    of 50-100ns, the raw CPU speed does not correlate linear with the GC speed).
+    Measurements give roughly 40-70ms for a full 400k newSpace 
+    (i.e. all objects survive).
+    The upper bounds of the scavenge blocking time can be controlled by changing
+    the size of the newSpace - ether via acommand line argument, or even dynamically
+    by Objectmemory>>newSpaceSize:. Smaller sizes lead to shorter blocking periods,
+    but greater absolute GC overhead. The default (400k) seems to be a good compromise.
+    (if you are not happy with it, try playing around with the settings)
+
+  oldSpace:
 
     To reclaim oldspace, the system uses three algorithms: mark&sweep, a copying
     (and compressing) baker-type collector and an incremental mark&sweep.
@@ -244,15 +311,23 @@
      memory, the compress is pretty fast).
 
     The incremental mark&sweep runs in the background, whenever the system is idle
-    (see ProcessorSceduler>>waitForEventOrTimeout), or alternatively as a low priority
-    background process (see ObjectMemory>>startBackgroundCollector). 
+    (see ProcessorSceduler>>waitForEventOrTimeout), or alternatively as a low or high
+    priority background process (see ObjectMemory>>startBackgroundCollector). 
     Like the normal mark&sweep, this incremental collector follows object references 
     and marks reachable objects on its way. However, this is done 'a few objects-at-a-time',
     to not disrupt the system noticably. Currently, there are some (theoretical) and in
-    practice never occurring situations, in which the incremental GC still creates delays
-    longer than a few milliseconds. A current project is involved with this and a future
-    versions of ST/X (ST/X-RT) will be available which shows deterministic worst case
-    behavior in its GC pauses (this will be provided as an additional add-on option).
+    practice never occurring situations, in which the incremental GC still creates noticable
+    delays. A current project is involved with this and a future versions of ST/X (ST/X-RT)
+    will be available which shows deterministic worst case behavior in its GC pauses 
+    (this will be provided as an additional add-on option - certainly not for free ;-).
+    Currently, incremental GC blockings are in the order of 10-70ms.
+    There is one catch with low priority background IGC: if there is never any idle
+    time available (i.e. all processes run all the time), it would never get a chance
+    to do any collection work. To handle this case, a background IGC can also be started
+    as a high priority process, which gives up the cpu (by delaying on the time) after
+    every IGC step. A high priority background collector will always make progress
+    and eventually finish a GC cycle. However, it may have more of an influence on 
+    the other processes. So, its up to you, to decide ...
 
     Incremental garbage collection is controlled by the variables 
     'IncrementalGCLimit', 'FreeSpaceGCLimit' and 'FreeSpaceGCAmount':
@@ -265,17 +340,17 @@
       'FreeSpaceGCAmount' more bytes are requested from the memory manager.
 
     The defaults are set in ObjectMemory>>initialize and can be changed in your 
-    startup 'smalltalk.rc'-file. Setting them to nil will turn incremental GC off.
+    startup 'smalltalk.rc'-file. Setting them to nil turns incremental GC off.
 
     For example, setting 'IncrementalGCLimit' to 500000 will start the background collector
     whenever 500k bytes have been allocated - usually very seldom. Setting it to some
     small number (say 10000) will have it run very often.
 
     Setting 'FreeSpaceGCAmount' to (say) 1meg lets the system try to always keep
-    1meg of freeSpace. If less memory is available, more oldSPace will be allocated
-    for. This may prevent the system from running into a GC pause when memory is
-    allocated in peaks (but only, if the incremental GC can keep up with allocation
-    rate). The trigger level 'FreeSpaceGCLimit' should be below that amount;
+    1meg of freeSpace. If less memory is available, more oldSpace will be allocated.
+    Keeping some memory in the pocket may prevent the system from running into a blocking
+    GC if memory is allocated in peaks (but only, if the incremental GC can keep up with 
+    allocation rate). The trigger level 'FreeSpaceGCLimit' should be below that amount;
     to avoid excessive incremental GC activity (say 1/4 if the amount).
 
     Having the background GC running often should not hurt the performance of your 
@@ -305,6 +380,78 @@
     space. (be prepared for changes in the future and make your application
     independ of the VM internals)
 
+  default setup:
+
+    The following table lists some default settings and means for changing them:
+
+	    what        default     change by           
+				    command line arg    dynamically
+    -----------------------------------------------------------------------
+	newSpace size     400k      -Mnew nnn           newSpaceSize:nnn
+
+	oldSpace size    3000k      -Mold nnn           moreOldSpace:
+							announceSpaceNeed:
+							collectGarbage
+
+	max tenure age     29                           lockTenure:
+							avoidTenure:
+							(sets it to infinity)
+        
+	adaptive tenure                                 tenureParameters
+
+	oldSpace
+	compressor      enabled     -Msingle            -
+
+	limit for
+	old-compress     8000k      -                   oldSpaceCompressLimit:
+
+	chunk size
+	to increase
+	oldpPace          256k      -                   oldSpaceIncrement:
+        
+	prefer moreOld
+	to doing GC      false      -                   fastMoreOldSpaceAllocation:
+
+	limit for
+	above                -      -                   fastMoreOldSpaceLimit:
+
+	keep size for        -      -                   freeSpaceGCAmount:
+	IGC
+
+	low water
+	trigger for IGC      -      -                   freeSpaceGCLimit:
+
+	allocated
+	trigger for IGC   500k      -                   incrementalGCLimit
+
+        
+    By default, no incremental GC activity is started.
+    You have to change your startup files to do this. A suggested configuration
+    (used by the author) is:
+
+	' keep 1meg in the pocket '
+
+	ObjectMemory freeSpaceGCAmount:1000000. 
+
+	' start incrementalGC when freespace drops below 250k '
+	' or 500k of oldSpace has been allocated              '
+
+	ObjectMemory freeSpaceGCLimit:250000.                 '
+	ObjectMemory incrementalGCLimit:500000.               '
+
+	' collect as a background process (the default is: at idle times)
+	' this means that running cubes or other demo processes are suspended
+	' for the collect; change the prio to below 4 if you want them to continue
+
+	ObjectMemory startBackgroundCollectorAt:5.            '
+	ObjectMemory startBackgroundFinalizationAt:5.         '
+
+	' quickly allocate more space (i.e. avoid blocking collects)
+	' up to 8meg - then start to collect if more memory is needed.
+
+	ObjectMemory fastMoreOldSpaceLimit:8*1024*1024.       '
+	ObjectMemory fastMoreOldSpaceAllocation:true.         '
+
   hints & tricks:
 
     normally, there is no need to call for an explicit garbage collection, or
@@ -416,7 +563,7 @@
 
     Let me know about additional special features you think are useful, and about
     special features you are using - this provides the feedback required to decide
-    which methods are to be removed or kept or enhanced in future versions.
+    which methods are to be removed, kept or enhanced in future versions.
 "
 ! !
 
@@ -1588,11 +1735,14 @@
 
 incrementalGC
     "perform one round of incremental GC steps.
-     The overall effect of this method is the same as calling markAndSweep.
-     However, #incrementalGC is interruptable while #markAndSweep
-     is atomic and blocks for a while. 
-     Thus this method can be called from a low prio (background) process 
-     to collect without disturbing foreground processes too much.
+     The overall effect of this method is (almost) the same as calling 
+     markAndSweep. However, #incrementalGC is interruptable while #markAndSweep
+     is atomic and blocks for a while. The code here performs incremental
+     GC steps, until one complete gc-cycle is completed. If running at a higher
+     than userBackground priority, it will give up the CPU after every such
+     step for a while.
+     Thus this method can be called either from a low prio (background) process 
+     or from a high prio process.
      (however, if you have nothing else to do, its better to call for markAndSweep,
       since it is faster)
      For example, someone allocating huge amounts of memory could
@@ -1601,12 +1751,21 @@
      cases, this can avoid a pause (in the higher prio processes) due to 
      a blocking GC."
 
-    [self gcStep] whileFalse:[].
+    |p delay|
+
+    Processor activeProcess priority > Processor userBackgroundPriority ifTrue:[
+	delay := Delay forMilliseconds:1
+    ].
+
+    [self gcStep] whileFalse:[
+	delay notNil ifTrue:[delay wait]
+    ].
     self moreOldSpaceIfUseful
 
     "
      ObjectMemory incrementalGC
      [ObjectMemory incrementalGC] forkAt:3
+     [ObjectMemory incrementalGC] forkAt:9 
     "
 !
 
@@ -2111,7 +2270,10 @@
      (i.e. oldSpace becomes full) and be forced to perform a full mark&sweep
      or even a compressing collect - making the overall realtime behavior worse.
      Use this only for special purposes or when realtime behavior
-     is required for a limited time period."
+     is required for a limited time period.
+
+     OBSOLETE: this is no longer supported 
+	       - it may be a no-operation by the time you read this."
 
 %{  /* NOCONTEXT */
     __allocForceSpace(OLDSPACE);