All Manuals > LispWorks® User Guide and Reference Manual > 11 Memory Management

11.6 Common Memory Management Features

This section summarizes Memory Management functionality common to all LispWorks 8.0 implementations.

11.6.1 Timing the garbage collector

The macro extended-time is useful when timing the Garbage Collector (GC).

Use start-gc-timing, stop-gc-timing and get-gc-timing to time GC operations.

11.6.2 Reducing image size

To reduce the size of the whole image, use clean-down.

In 32-bit LispWorks, you can use (clean-down) or the less aggressive (clean-down nil) to reduce the image size when the image is much larger than the amount that is allocated. In 64-bit LispWorks there is no need to do that.

(clean-down t) promotes to generation 3 and tries to reduce the image size, while (clean-down nil) promotes only to generation 2 and does not reduce the image size. Experience suggests that the latter is actually more useful in most circumstances.

In some circumstances it is important to avoid enlarging the size of the image even temporarily. The common situation is when the operating system signals low memory. In this situation you should use reduce-memory instead of clean-down.

11.6.3 Allocation of interned symbols and packages

Interned symbols (and their symbol names), and packages, are treated in a special way, because they are assumed to have a long life. They are allocated in the generation specified by the variable *symbol-alloc-gen-num*, which has the initial value 2 in 32-bit LispWorks and 3 in 64-bit LispWorks.

Symbols created with make-symbol or gensym start out in generation 0.

Symbols will be garbage collected if they are no longer accessible (regardless of property lists) but note that in 32-bit LispWorks, if the symbols are in generation 2 then you might need to invoke gc-generation explicitly to collect them in a timely manner.

11.6.4 Allocation of stacks

Stacks are allocated directly in generation 2 because they are relatively expensive to promote. Therefore creating many processes will cause generation 2 to grow, even if these processes are short-lived.

The variable *default-stack-group-list-length* controls the number of stacks that are cached for reuse. Increase its value if your application repeatedly makes and discards more than 10 processes.

11.6.5 Mapping across all objects

To call a function on all objects in the image, use sweep-all-objects.

11.6.6 Special actions

You may want to perform special actions when certain types of object are garbage collected, using the functions add-special-free-action, flag-special-free-action, flag-not-special-free-action and remove-special-free-action.

For example, when an open file stream is garbage collected, the file descriptor must be closed. This operation is performed as a special action.

Note: You should not rely on special free actions for objects with a high turn-over rate (that is, where many such objects are allocated and they become garbage fairly quickly), because some may not get collected early enough. Either ensure that the cleanup is called elsewhere, or arrange for a GC to happen.

11.6.7 Garbage collection of foreign objects

Users of the Foreign Language Interface may want to specify the allocation of static arrays. The recommended way to do this is to call make-array with :allocation :static. See for example :lisp-array in the Foreign Language Interface User Guide and Reference Manual.

11.6.8 Freeing of objects by the GC

Weak arrays and weak hash tables can be used to allow the GC to free objects.

Relevant functions are make-hash-table, set-hash-table-weak, set-array-weak, make-array and copy-to-weak-simple-vector.

For a description of weak vectors see set-array-weak.

11.6.9 Assisting the garbage collector

This section describes techniques that may improve the performance of your application by reducing the GC's workload. Breaking pointers from older objects

This is a technique that can be useful when older objects regularly point to newer objects in a lower generation. In such a case, when the lower generation (only) is collected these newer objects will be promoted even if the older objects are not live. All of these objects will not get collected until the higher generation is collected.

This is a general issue with generational garbage collection and, if it causes poor performance in your application, can be addressed along these lines. It is not necessarily a problem in every case where older objects point to newer objects.

For example, suppose you are popping items from a queue represented as a list of conses (or other structures), then you can set the "next" slot of each popped item to nil.

In the code below, if the queue-head cons is promoted to generation n, then all the other conses will also be promoted to generation n eventually, until generation n is collected. This happens even after calls to pop-queue have removed these conses from the queue.

(defstruct queue head tail)
(defun push-queue (item queue)
  (let ((new (cons item nil)))
     (if (queue-head queue)
         (setf (cdr (queue-tail queue)) new)
       (setf (queue-head queue) new))
     (setf (queue-tail queue) new)))
(defun pop-queue (queue)
  (pop (queue-head queue)))

The fix is to make pop-queue set the "next" slot (in this case the cdr) of the discarded queue-head cons to nil, so that it no longer points from an older object to a newer object. For example:

(defun pop-queue (queue)
   (when-let (head (queue-head queue))
     (setf (queue-head queue) (shiftf (cdr head) nil))
     (car head)))

LispWorks® User Guide and Reference Manual - 01 Dec 2021 19:30:20