@@ -835,6 +835,7 @@ struct heap_page {
835835 struct heap_page * free_next ;
836836 RVALUE * start ;
837837 RVALUE * freelist ;
838+ RVALUE * freelist_tail ;
838839 struct list_node page_node ;
839840
840841 bits_t wb_unprotected_bits [HEAP_PAGE_BITMAP_LIMIT ];
@@ -1658,14 +1659,16 @@ heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj
16581659 RVALUE * p = (RVALUE * )obj ;
16591660 asan_unpoison_memory_region (& page -> freelist , sizeof (RVALUE * ), false);
16601661
1661- if (page -> freelist ) {
1662- page -> freelist -> as .free .prev = p ;
1663- }
1664-
16651662 p -> as .free .flags = 0 ;
1666- p -> as .free .prev = NULL ;
1667- p -> as .free .next = page -> freelist ;
1668- page -> freelist = p ;
1663+ if (!page -> freelist ) {
1664+ page -> freelist = p ;
1665+ }
1666+ if (page -> freelist_tail ) {
1667+ page -> freelist_tail -> as .free .next = p ;
1668+ }
1669+ p -> as .free .prev = page -> freelist_tail ;
1670+ p -> as .free .next = NULL ;
1671+ page -> freelist_tail = p ;
16691672 asan_poison_memory_region (& page -> freelist , sizeof (RVALUE * ));
16701673
16711674 if (RGENGC_CHECK_MODE &&
@@ -1969,12 +1972,15 @@ heap_increment(rb_objspace_t *objspace, rb_heap_t *heap)
19691972 gc_report (1 , objspace , "heap_increment: heap_pages_sorted_length: %d, heap_pages_inc: %d, heap->total_pages: %d\n" ,
19701973 (int )heap_pages_sorted_length , (int )heap_allocatable_pages , (int )heap -> total_pages );
19711974
1975+ printf ("heap_increment: heap_allocatable_pages: %d\n" , heap_allocatable_pages );
1976+
19721977 GC_ASSERT (heap_allocatable_pages + heap_eden -> total_pages <= heap_pages_sorted_length );
19731978 GC_ASSERT (heap_allocated_pages <= heap_pages_sorted_length );
19741979
19751980 heap_assign_page (objspace , heap );
19761981 return TRUE;
19771982 }
1983+ printf ("HEAP ALLOCATABLE PAGES NONE\n" );
19781984 return FALSE;
19791985}
19801986
@@ -2014,6 +2020,7 @@ heap_get_freeobj_from_next_freepage(rb_objspace_t *objspace, rb_heap_t *heap)
20142020 asan_unpoison_memory_region (& page -> freelist , sizeof (RVALUE * ), false);
20152021 p = page -> freelist ;
20162022 page -> freelist = NULL ;
2023+ page -> freelist_tail = NULL ;
20172024 asan_poison_memory_region (& page -> freelist , sizeof (RVALUE * ));
20182025 page -> free_slots = 0 ;
20192026 asan_unpoison_object ((VALUE )p , true);
@@ -2033,18 +2040,17 @@ remove_obj_from_freelist(rb_heap_t *heap, VALUE obj)
20332040 prev -> as .free .next = next ;
20342041 }
20352042
2036- if (p == heap -> freelist ) {
2037- GC_ASSERT (prev == NULL );
2038- heap -> freelist = next ;
2043+ if (next ) {
2044+ next -> as .free .prev = prev ;
20392045 }
20402046
2041- if (p == GET_HEAP_PAGE ( p ) -> freelist ) {
2047+ if (p == heap -> freelist ) {
20422048 GC_ASSERT (prev == NULL );
2043- GET_HEAP_PAGE ( p ) -> freelist = next ;
2049+ heap -> freelist = next ;
20442050 }
20452051
2046- if (next ) {
2047- next -> as . free . prev = prev ;
2052+ if (p == GET_HEAP_PAGE ( p ) -> freelist || p == GET_HEAP_PAGE ( p ) -> freelist_tail ) { // ?
2053+ rb_bug ( "NOPE" ) ;
20482054 }
20492055}
20502056
@@ -2702,6 +2708,8 @@ obj_free_object_id(rb_objspace_t *objspace, VALUE obj)
27022708static int
27032709obj_free (rb_objspace_t * objspace , VALUE obj )
27042710{
2711+ GC_ASSERT (BUILTIN_TYPE (obj ) != T_GARBAGE );
2712+
27052713 RB_DEBUG_COUNTER_INC (obj_free );
27062714
27072715 gc_event_hook (objspace , RUBY_INTERNAL_EVENT_FREEOBJ , obj );
@@ -4189,6 +4197,7 @@ type_sym(size_t type)
41894197 COUNT_TYPE (T_ICLASS );
41904198 COUNT_TYPE (T_ZOMBIE );
41914199 COUNT_TYPE (T_MOVED );
4200+ COUNT_TYPE (T_GARBAGE );
41924201#undef COUNT_TYPE
41934202 default : return SIZET2NUM (type ); break ;
41944203 }
@@ -4235,6 +4244,7 @@ count_objects(int argc, VALUE *argv, VALUE os)
42354244 rb_objspace_t * objspace = & rb_objspace ;
42364245 size_t counts [T_MASK + 1 ];
42374246 size_t freed = 0 ;
4247+ size_t garbage = 0 ;
42384248 size_t total = 0 ;
42394249 size_t i ;
42404250 VALUE hash = Qnil ;
@@ -4270,7 +4280,12 @@ count_objects(int argc, VALUE *argv, VALUE os)
42704280 asan_poison_object (vp );
42714281 }
42724282
4273- p += obj_slot_stride ((VALUE )p );
4283+ if (BUILTIN_TYPE (p ) == T_GARBAGE ) {
4284+ garbage += p -> as .garbage .length ;
4285+ p += p -> as .garbage .length ;
4286+ } else {
4287+ p ++ ;
4288+ }
42744289 }
42754290 total += page -> total_slots ;
42764291 }
@@ -4282,6 +4297,7 @@ count_objects(int argc, VALUE *argv, VALUE os)
42824297 rb_hash_stlike_foreach (hash , set_zero , hash );
42834298 }
42844299 rb_hash_aset (hash , ID2SYM (rb_intern ("TOTAL" )), SIZET2NUM (total ));
4300+ rb_hash_aset (hash , ID2SYM (rb_intern ("GARBAGE" )), SIZET2NUM (garbage ));
42854301 rb_hash_aset (hash , ID2SYM (rb_intern ("FREE" )), SIZET2NUM (freed ));
42864302
42874303 for (i = 0 ; i <= T_MASK ; i ++ ) {
@@ -4398,11 +4414,6 @@ gc_page_sweep(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *sweep_
43984414 heap_page_add_freeobj (objspace , sweep_page , vp );
43994415 gc_report (3 , objspace , "page_sweep: %s is added to freelist\n" , obj_info (vp ));
44004416
4401- // Demo freeing garbage slots - remove me later
4402- if (NUM_IN_PAGE (p + 1 ) < GET_PAGE_HEADER (p )-> page -> total_slots &&
4403- BUILTIN_TYPE (p + 1 ) == T_GARBAGE ) {
4404- freed_slots += gc_free_garbage (objspace , p + 1 );
4405- }
44064417
44074418 freed_objects ++ ;
44084419 freed_slots ++ ;
@@ -6619,6 +6630,10 @@ gc_marks_finish(rb_objspace_t *objspace)
66196630 /* decide full GC is needed or not */
66206631 rb_heap_t * heap = heap_eden ;
66216632 size_t total_slots = heap_allocatable_pages * HEAP_PAGE_OBJ_LIMIT + heap -> total_slots ;
6633+ // FIXME: number of free slots (i.e. swept slots) is not equal to total slots - marked slots (this does not account for garbage slots).
6634+ // This causes the number of free slots to seem much larger than there actually are, so `heap_extend_pages` does not add more pages.
6635+ // When no more pages are added, GC is kicked off much more frequently which causes a lot of slowdown.
6636+ // Solution idea: keep track of number of free & garbage slots we encounter during sweeping.
66226637 size_t sweep_slots = total_slots - objspace -> marked_slots ; /* will be swept slots */
66236638 size_t max_free_slots = (size_t )(total_slots * gc_params .heap_free_slots_max_ratio );
66246639 size_t min_free_slots = (size_t )(total_slots * gc_params .heap_free_slots_min_ratio );
0 commit comments