|Version 1 (modified by gb, 6 years ago) (diff)|
It may be useful in some applications to know that a full GC will happen "soon" (for some value of "soon".) It's not generally possible to execute Lisp code when a memory allocation attempt results in GC, but it can detect case where an allocation causes the amount of "free memory before the next full GC" falls below a user-specified threshold and to call a user-defined function when this event occurs. This mechanism is implemented (for x8632 and x8664) in the CCL trunk as of r13882 and, if it proves useful, the general plan is to incorporate it into the next release and support it on all platforms.
The CCL kernel's view of memory allocation
Lisp objects (CONS cells, etc.) are always allocated within thread-private "chunks"; a "chunk" is just a contiguous range of memory whose size (by default) is a multiple of 64Kb (on 32-bit platform) or 128Kb (on 64-bit platforms.) A thread allocates objects within its current chunk; if there isn't enough free memory in a chunk to satisfy an allocation attempt, the thread traps into the lisp kernel, which tries to assign one or more previously free chunks to the thread so that the object allocation can be completed. If enough free chunks aren't available when this trap occurs, the kernel tries to make more (by invoking the GC and/or mapping more memory from the OS.) From the kernel's point of view, lisp heap memory is "allocated" in these chunk-sized units; it's only involved in object allocation when an object allocation attempt causes a trap.
As chunks are allocated, the amount of free memory before the next full GC (as reported by ROOM and obtained by CCL::%FREBYTES) decreases (in chunk-sized increments.) If chunk allocation causes this value to be decremented below a specified threshold, the kernel will call a user-defined function after the object allocation completes.
There are some cases - when very large objects are allocated - where free space is above the notification threshold when the object's allocated but the object allocation can only succeed by invoking the GC. The mechanism described here can't call out to the notification function before the GC occurs in this case (and it'd be pointless to call it after the GC occurs.)
Specifiying and getting the notification threshold
(SET-GC-NOTIFICATION-THRESHOLD value) [Function]
Sets the kernel variable ("the pre-gc notification threshold") to the specified value. Useful behavior depends on this value being greater than the allocation "chunk" size (which can be obtained by calling (CCL:DEFAULT-ALLOCATION-QUANTUM)) and smaller than the GC threshold.
Obtains the value of the kernel's pre-gc notification threshold variable. This value is preserved by SAVE-APPLICATION; it's initially 0, which means that no pre-GC notification will occur.
*PENDING-GC-NOTIFICATION-HOOK* [Special Variable]
The default value of this variable is NIL. If it's set or bound to a non-null value, that value should be a function of 0 arguments; that function will be called when free space in the lisp heap drops below the GC notification threshold (as described above.)
The notification function is called on the same thread that caused the allocation trap as soon as the triggering object allocation's completed.
;;; Not necessarily a plausible or interesting example ...
(defun cons-until-1mb-free () (let* ((current-threshold (get-gc-notification-threshold))) (unwind-protect (progn (set-gc-notification-threshold (ash 1 20)) (let* ((*pending-gc-notification-hook* (lambda () (format t "~&No more consing!") (return-from cons-until-1mb-free nil))) (l ())) (loop (push nil l)))) (set-gc-notification-threshold current-threshold))))