[Sbcl-help] How to catch storage-condition without entering to low level debugger?

47 views
Skip to first unread message

Dmytro Bilous

unread,
Aug 1, 2018, 11:05:54 AM8/1/18
to sbcl...@lists.sourceforge.net

Hi All,


I use "storage-condition" to deal with allocation problems. But sometimes the low-level debugger is entered. May be the system fails to raise the condition because of no enough memory.  Can I do proper exception handling in such situation instead of crashing or entering to low level debugger? Can I lock some memory for exception handling to be sure that storage-condition is able to raise properly?


I wrote very simple program that reproduce the issue.


(defun test-storage-condition ()
  (handler-case
      (loop for i from 1 to (* 1024 1024 1024) collect 0)
    (storage-condition (err)
      (gc :full t)
      (print err))))

(test-storage-condition)


I got following:

* Heap exhausted during garbage collection: 0 bytes available, 16 requested.
 Gen StaPg UbSta LaSta LUbSt Boxed Unboxed LB   LUB  !move  Alloc  Waste   Trig    WP  GCs Mem-age
   0:     0     0     0     0     0     0     0     0     0        0     0 10737418    0   0  0,0000
   1: 22088     0     0     0 16398     0     0     0     0 537263792 65872 333170122    0   1  1,4003
   2: 32767     0     0     0 13967     0     0     0     9 457502640 168016 10737418    0   0  0,0000
   3:     0     0     0     0     0     0     0     0     0        0     0 10737418    0   0  0,0000
   4:     0     0     0     0     0     0     0     0     0        0     0 10737418    0   0  0,0000
   5:     0     0     0     0   469    75     0    10    60 17590640 562832 28328058  410   1  0,0000
   6:     0     0     0     0  1690   159     0     0     0 60588032     0  2000000 1588   0  0,0000
   Total bytes allocated    = 1072945104
   Dynamic-space-size bytes = 1073741824
GC control variables:
   *GC-INHIBIT* = true
   *GC-PENDING* = true
   *STOP-FOR-GC-PENDING* = false
fatal error encountered in SBCL pid 12610(tid 140737321367296):
Heap exhausted, game over.

Error opening /dev/tty: No such device or address
Welcome to LDB, a low-level debugger for the Lisp runtime environment.
ldb>


--

Thanks,

Dmytro Bilous

Stas Boukarev

unread,
Aug 1, 2018, 4:40:06 PM8/1/18
to Dmytro Bilous, sbcl...@lists.sourceforge.net
When the LDB is reached there's nothing to be done at that point, there is no more space to allocate anything, and it's in the middle of GC, the heap is not in a consistent state.

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot_______________________________________________
Sbcl-help mailing list
Sbcl...@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sbcl-help

Dmytro Bilous

unread,
Aug 6, 2018, 5:09:28 AM8/6/18
to Stas Boukarev, sbcl...@lists.sourceforge.net

I try to avoid entering to LDB and try to find out a way to handle this type of error inside application. I wrote small program that just reproduce real workload in my application. This example is getting bitten by generational trap.


(define-alien-routine print-generation-stats void)
(defun test-gen-trap()
  (labels ((get-array (asize)
             (make-array asize :element-type '(unsigned-byte 8) :initial-element 0))
         (get-list (len asize)
           (loop for i from 1 to len collect (get-array asize))))
  (loop for i from 1 to 1024 do
       (format t "Iteration ~a~%" i)
       (print-generation-stats)
       (get-list 80 (* 1024 1024))
       (get-list 1024 1024))))
(test-gen-trap)

Some time I get storage-condition but sometime I fall down to LDB.

I try to avoid to call (gc :full t) directly from code because I have a multi-threaded application and if every thread will call full gc then performance dramatically decreased


Questions:

1. Can I tuning GC to reserve memory for condition handling?

2. Can I tuning GC to call storage-condition early (when some amount of free memory still exists)?

3. Can I tuning GC to call garbage collection across all generations automatically when low memory?

3. Are there any other solutions?


From: Stas Boukarev <stas...@gmail.com>
Sent: Wednesday, August 1, 2018 11:39:31 PM
To: Dmytro Bilous
Cc: sbcl...@lists.sourceforge.net
Subject: Re: [Sbcl-help] How to catch storage-condition without entering to low level debugger?
 

Dmytro Bilous

unread,
Aug 7, 2018, 11:15:45 AM8/7/18
to sbcl...@lists.sourceforge.net

Hi All,

I have tried to run "test-gen-trap" on different implementations. And figured out that Allegro Lisp call global GC when reach some memory limit.

So I try to do the same with after-gc-hooks. I set limit to 100M:


(setf sb-ext:*after-gc-hooks* nil)
(push (lambda ()
        (let ((size (sb-ext:dynamic-space-size))
              (usage (sb-kernel:dynamic-usage)))
          (when (< (- size usage) (* 100 1024 2014))
            (format t "FULL GC~%")
            (gc :full t))
          (format t "size: ~a, usage: ~a~%" size usage)))
      sb-ext:*after-gc-hooks*)
(print sb-ext:*after-gc-hooks*)

(define-alien-routine print-generation-stats void)
(defun test-gen-trap()
  (labels ((get-array (asize)
             (make-array asize :element-type '(unsigned-byte 8) :initial-element 0))
         (get-list (len asize)
           (loop for i from 1 to len collect (get-array asize))))
  (loop for i from 1 to 1024 do
       (format t "Iteration ~a~%" i)
       (print-generation-stats)
       (get-list 80 (* 1024 1024))
       (get-list 1024 1024))))
(test-gen-trap)


So now when available memory is less then 100M (gc :full t) will be called. Seems at least for now that should solve our issue.

--

Thanks,

Dmytro


From: Dmytro Bilous
Sent: Monday, August 6, 2018 11:37:16 AM
To: Stas Boukarev

Ilya Perminov

unread,
Aug 7, 2018, 2:30:41 PM8/7/18
to sbcl...@lists.sourceforge.net

Hi Dmytro,


SBCL has a couple of GC knobs you may want to try tuning: one is the number of generations and the other one is generation-minimum-age-before-gc.

With the default settings the amount of garbage SBCL may accumulate is something like 2 * the number of generations * size of new referenced data. The size of

new referenced data in your test is about 80Mb and your dynamic space size seems to be 1Gb. That is probably too tight.


Regards,
Ilya

Gunter Königsmann

unread,
Aug 7, 2018, 3:41:21 PM8/7/18
to Ilya Perminov, sbcl...@lists.sourceforge.net
The problem doesn't even need to be that a big percentage of your main memory is allocated by your program: if you alternate between allocating memory for temporary data and allocating space for list elements generated from this data freeing the temporary data might leave your memory in a fragmented state with no big contiguous blocks left...
--
Diese Nachricht wurde von meinem Android-Gerät mit K-9 Mail gesendet.

Ilya Perminov

unread,
Aug 7, 2018, 4:28:19 PM8/7/18
to Gunter Königsmann, sbcl...@lists.sourceforge.net
SBCL uses a copying collector, so memory fragmentation is not an issue.

Douglas Katzman via Sbcl-help

unread,
Aug 7, 2018, 4:46:08 PM8/7/18
to Ilya Perminov, SBCL help
On Tue, Aug 7, 2018 at 4:28 PM Ilya Perminov <iper...@dwavesys.com> wrote:
SBCL uses a copying collector, so memory fragmentation is not an issue.


While the spirit of what you said is true, is is not technically true that the collector is never subject to a fragmentation problem.

There are ways to be adversarial and make it unable to find free space though the sum of sizes of free blocks exceeds an allocation request.
Objects above a certain size are not copied, ever, after allocation. You can cause allocation patterns that put large objects in the way of other ranges of space that are just large enough, if only we would move the large object.  But we won't.
In practice I doubt this causes much loss of usable space, but I think users should be cautioned not to take the "is not an issue" to be gospel.
A change I'd like to see in the GC is that we should _try_ never to move large objects, but we should not _forbid_ moving them. The amount by which we should try not to move should be proportional to how large the object is.

Gunter Königsmann

unread,
Aug 8, 2018, 12:07:24 AM8/8/18
to Douglas Katzman, Ilya Perminov, SBCL help
That might explain much: I was reading float numbers from a big CSV file into a list when the said out of memory happened to me: Loads of small strings and list items.

Thanks a lot,

Gunter.
Reply all
Reply to author
Forward
0 new messages