Hi!
I'm trying to understand what is happening with memory in next test cases:
1. Making many threads, but not staring them -------------------------------------------- Gambit v4.5.1
(gc-report-set! #t)
(##gc)
*** GC: 1 ms, 1.33M alloc, 2.14M heap, 654K live (30% 635200+34704)
(define t (do ((i 0 (+ 1 i)) (l '() (cons (make-thread (lambda ()
(void))) l))) ((> i 1000000) l))) *** GC: 2 ms, 2.82M alloc, 4.77M heap, 2.08M live (44% 2149968+34704) *** GC: 5 ms, 5.50M alloc, 10.0M heap, 4.76M live (47% 4953664+34704) *** GC: 16 ms, 10.8M alloc, 20.5M heap, 10.0M live (49% 10459472+34704) *** GC: 27 ms, 21.3M alloc, 41.5M heap, 20.5M live (49% 21468624+34704) *** GC: 54 ms, 42.3M alloc, 83.5M heap, 41.5M live (50% 43486848+34704) *** GC: 97 ms, 84.3M alloc, 168M heap, 83.5M live (50% 87527232+34704) *** GC: 200 ms, 168M alloc, 336M heap, 168M live (50% 175605712+34704) *** GC: 392 ms, 336M alloc, 672M heap, 336M live (50% 351765360+34704) *** GC: 801 ms, 672M alloc, 1.31G heap, 671M live (50% 704080736+34704)
(##gc)
*** GC: 1417 ms, 1.15G alloc, 2.30G heap, 1.15G live (50% 1232640240+34704)
(set! t #f) (##gc)
*** GC: 943 ms, 1.15G alloc, 2.30G heap, 1.15G live (50% 1232640192+34704)
2. Making many threads, starting them and waiting for termination ----------------------------------------------------------------- Gambit v4.5.1
(gc-report-set! #t)
(##gc)
*** GC: 3 ms, 1.33M alloc, 2.14M heap, 654K live (30% 635200+34704)
(define t (do ((i 0 (+ 1 i)) (l '() (cons (make-thread (lambda ()
(void))) l))) ((> i 1000000) l))) *** GC: 4 ms, 2.83M alloc, 4.77M heap, 2.12M live (44% 2187824+34704) *** GC: 6 ms, 5.47M alloc, 10.0M heap, 4.76M live (47% 4953664+34704) *** GC: 14 ms, 10.7M alloc, 20.5M heap, 10.0M live (49% 10460704+34704) *** GC: 26 ms, 21.2M alloc, 41.5M heap, 20.5M live (49% 21468624+34704) *** GC: 51 ms, 42.2M alloc, 83.5M heap, 41.5M live (50% 43486848+34704) *** GC: 97 ms, 84.2M alloc, 168M heap, 83.5M live (50% 87527232+34704) *** GC: 199 ms, 168M alloc, 336M heap, 168M live (50% 175605712+34704) *** GC: 396 ms, 336M alloc, 672M heap, 336M live (50% 351765360+34704) *** GC: 814 ms, 672M alloc, 1.31G heap, 671M live (50% 704080736+34704)
(##gc)
*** GC: 1404 ms, 1.15G alloc, 2.30G heap, 1.15G live (50% 1232640240+34704)
(for-each (lambda (x) (thread-start! x) (thread-join! x)) t) (##gc)
*** GC: 660 ms, 1.55G alloc, 1.16G heap, 596M live (50% 624635824+34704)
(set! t #f) (##gc)
*** GC: 133 ms, 1.55G alloc, 2.14M heap, 654K live (30% 635200+34704)
--------------------------------------------------------------------- The question is: why GC does not collect unreferenced thread objects in first case and do collect them in second case?
Vasil
Afficher les réponses par date
On 29-Aug-09, at 7:38 AM, vasil wrote:
The question is: why GC does not collect unreferenced thread objects in first case and do collect them in second case?
Vasil
Gambit implements the concept of "thread groups" (which is an extension of SRFI 18 and 21). When a thread is created, by default it is put in the same thread group as the parent thread. It will stay in the thread group until it terminates. So if a thread is not started it will stay in the thread group of its parent. Because the parent thread in your example does not terminate (it is the primordial thread), the garbage collector does not reclaim the thread group (which would have reclaimed the threads that were not started).
For example:
% gsi Gambit v4.5.1
(thread-group->thread-list (thread-thread-group (current-thread)))
(#<thread #1 primordial>)
(define t (do ((i 0 (+ 1 i)) (l '() (cons (make-thread (lambda ()
(void))) l))) ((> i 5) l)))
(thread-group->thread-list (thread-thread-group (current-thread)))
(#<thread #1 primordial> #<thread #2> #<thread #3> #<thread #4> #<thread #5> #<thread #6> #<thread #7>)
(for-each thread-start! t) (thread-group->thread-list (thread-thread-group (current-thread)))
(#<thread #1 primordial>)
To get around this, you could create each thread in its own thread group:
% gsi Gambit v4.5.1
(gc-report-set! #t) (define t (do ((i 0 (+ 1 i)) (l '() (cons (make-thread (lambda ()
(void)) 'name1 (make-thread-group 'name2 #f)) l))) ((> i 10000) l))) *** GC: 1 ms, 348K alloc, 206K heap, 64.1K live (31% 43032+22620) *** GC: 1 ms, 488K alloc, 654K heap, 202K live (31% 184184+22620) *** GC: 2 ms, 937K alloc, 1.51M heap, 650K live (42% 642616+22620) *** GC: 6 ms, 1.79M alloc, 3.26M heap, 1.51M live (46% 1558648+22620) *** GC: 14 ms, 3.55M alloc, 6.76M heap, 3.26M live (48% 3394872+22620) *** GC: 21 ms, 7.05M alloc, 13.8M heap, 6.76M live (49% 7063160+22620)
(set! t #f) (##gc)
*** GC: 2 ms, 8.28M alloc, 206K heap, 43.7K live (21% 22096+22620)
Marc
Thanks, Marc!
Everything is clear for me now.
Vasil.
Marc Feeley wrote:
On 29-Aug-09, at 7:38 AM, vasil wrote:
The question is: why GC does not collect unreferenced thread objects in first case and do collect them in second case?
Vasil
Gambit implements the concept of "thread groups" (which is an extension of SRFI 18 and 21). When a thread is created, by default it is put in the same thread group as the parent thread. It will stay in the thread group until it terminates. So if a thread is not started it will stay in the thread group of its parent. Because the parent thread in your example does not terminate (it is the primordial thread), the garbage collector does not reclaim the thread group (which would have reclaimed the threads that were not started).
For example:
% gsi Gambit v4.5.1
(thread-group->thread-list (thread-thread-group (current-thread)))
(#<thread #1 primordial>)
(define t (do ((i 0 (+ 1 i)) (l '() (cons (make-thread (lambda ()
(void))) l))) ((> i 5) l)))
(thread-group->thread-list (thread-thread-group (current-thread)))
(#<thread #1 primordial> #<thread #2> #<thread #3> #<thread #4> #<thread #5> #<thread #6> #<thread #7>)
(for-each thread-start! t) (thread-group->thread-list (thread-thread-group (current-thread)))
(#<thread #1 primordial>)
To get around this, you could create each thread in its own thread group:
% gsi Gambit v4.5.1
(gc-report-set! #t) (define t (do ((i 0 (+ 1 i)) (l '() (cons (make-thread (lambda ()
(void)) 'name1 (make-thread-group 'name2 #f)) l))) ((> i 10000) l))) *** GC: 1 ms, 348K alloc, 206K heap, 64.1K live (31% 43032+22620) *** GC: 1 ms, 488K alloc, 654K heap, 202K live (31% 184184+22620) *** GC: 2 ms, 937K alloc, 1.51M heap, 650K live (42% 642616+22620) *** GC: 6 ms, 1.79M alloc, 3.26M heap, 1.51M live (46% 1558648+22620) *** GC: 14 ms, 3.55M alloc, 6.76M heap, 3.26M live (48% 3394872+22620) *** GC: 21 ms, 7.05M alloc, 13.8M heap, 6.76M live (49% 7063160+22620)
(set! t #f) (##gc)
*** GC: 2 ms, 8.28M alloc, 206K heap, 43.7K live (21% 22096+22620)
Marc