[racket] Yet another garbage collection question: major GC cycles
Dear Racketeers,
*SMALL PREAMBLE*
I'm trying to understand the observed GC behavior so I can create better
application performance, and I'm focusing on the major GC-cycle.
For usability, my desire would be to have a major-garbage-collection
cycle last about the same time as a typical request-response cycle, so
that users experience a maximum 2x normal delay if a request-response
cyle is interrupted by a major GC-cycle.
Please feel free to direct me to a paper if its simpler.
*THE OBSERVED RACKET BEHAVIOR*
When an application is compiled and run, the garbage collector in v5.3.2
appears to run through a number of minor collection cycles, then will
execute two sequential major cycles, in rapid succession.
(I assume this description of behavior is not controversial. For an
example, please compile and run the source code below.)
The major cycles take a finite amount of time, such duration unrelated
to the amount of memory cleaned up. This seems consistent with earlier
descriptions by Matthias that the GC walks through memory looking for
unreachable code.
The duration of the major cycle seems directly related to the amount of
memory use by the running application, which again, is consistent with
prior GC descriptions on this board.
Given the amount of memory usage as an independent vairable, I believe I
could closely estimate the duration of a major GC cycle (for a specific
piece of hardware - the constant 3.5ms per 80MB yields good results).
After the major cycles are done, even if no additional user activity
occurs, the garbage collector will periodically run two sequential major
cycles, which recover little or no memory.
In contrast, frequency of minor cycles are clearly user-activity
dependent.
*THE QUESTIONS*
My questions are
1) Why are two successive major cycles neccesary?
2) Why do major cycles occur in the absence of activity? Is there a
timer associated with major cycles?
3) Is there an optimization going on between amount of memory
consumption that triggers the first major cycle and Racket's estimate of
the RATE of memory consumption? (fast as. frequent vs. slow and
infrequent)
4) is there any rate-related term in the GC algorithm?
5) How does Racket determine how much memory to ask the operating system
to allocate to an executable?
Thanks
R./
Zack
EXAMPLE CODE
#lang web-server
(require web-server/servlet-env)
(struct gc-info (major? pre-amount pre-admin-amount code-amount
post-amount post-admin-amount
start-process-time end-process-time
start-time end-time)
#:prefab)
(define log-rcvr (make-log-receiver (current-logger) 'debug 'GC))
(thread (λ _ (letrec ((rfc (λ _ (let ((gc (vector-ref (sync log-rcvr)
2)))
(printf "\nMajor cycle:? ~A ;
cleaned-up ~A MB in ~A ms\n"
(gc-info-major? gc)
(floor (/ (-
(gc-info-pre-amount gc) (gc-info-post-amount gc)) (* 1024 1024)))
(-
(gc-info-end-process-time gc) (gc-info-start-process-time gc)))
(rfc)))))
(rfc))))
(define (start request)
(letrec ((response-generator (λ (make-url)
(displayln 'clicky)
(response/xexpr `(html (head)
(body (a ((href
,(format "~A" (make-url receive-request)))) "click me"))))))
(receive-request (λ (request)
(response/xexpr `(html (head)
(body "Thank you.
We are done."))))))
(send/suspend/dispatch response-generator)))
(serve/servlet start
#:stateless? #t
#:launch-browser? #t
#:connection-close? #t
#:quit? #f
#:listen-ip #f
#:port 8000
#:servlet-path "/")