@@ -620,6 +620,7 @@ typedef struct rb_objspace {
620620
621621 size_t allocated_pages ;
622622 size_t freed_pages ;
623+ size_t large_pages ;
623624 uintptr_t range [2 ];
624625 size_t freeable_pages ;
625626
@@ -897,6 +898,7 @@ struct heap_page {
897898 unsigned int before_sweep : 1 ;
898899 unsigned int has_remembered_objects : 1 ;
899900 unsigned int has_uncollectible_wb_unprotected_objects : 1 ;
901+ unsigned int large : 1 ;
900902 } flags ;
901903
902904 rb_heap_t * heap ;
@@ -916,6 +918,10 @@ struct heap_page {
916918 /* If set, the object is not movable */
917919 bits_t pinned_bits [HEAP_PAGE_BITMAP_LIMIT ];
918920 bits_t age_bits [HEAP_PAGE_BITMAP_LIMIT * RVALUE_AGE_BIT_COUNT ];
921+
922+ /* Only meaningful when flags.large: the true byte size of the region
923+ * backing this page (a large object lives outside heaps[], one per page). */
924+ size_t large_region_size ;
919925};
920926
921927/*
@@ -1980,8 +1986,14 @@ heap_page_body_free(struct heap_page_body *page_body)
19801986static void
19811987heap_page_free (rb_objspace_t * objspace , struct heap_page * page )
19821988{
1983- objspace -> heap_pages .freed_pages ++ ;
1984- heap_page_body_free (page -> body );
1989+ if (RB_UNLIKELY (page -> flags .large )) {
1990+ objspace -> heap_pages .large_pages -- ;
1991+ gc_aligned_free (page -> body , page -> large_region_size );
1992+ }
1993+ else {
1994+ objspace -> heap_pages .freed_pages ++ ;
1995+ heap_page_body_free (page -> body );
1996+ }
19851997 free (page );
19861998}
19871999
@@ -2450,7 +2462,11 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
24502462size_t
24512463rb_gc_impl_obj_slot_size (VALUE obj )
24522464{
2453- return GET_HEAP_PAGE (obj )-> slot_size - RVALUE_OVERHEAD ;
2465+ struct heap_page * page = GET_HEAP_PAGE (obj );
2466+ if (RB_UNLIKELY (page -> flags .large )) {
2467+ return page -> large_region_size - sizeof (struct heap_page_header ) - RVALUE_OVERHEAD ;
2468+ }
2469+ return page -> slot_size - RVALUE_OVERHEAD ;
24542470}
24552471
24562472static inline size_t
@@ -2800,6 +2816,73 @@ newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace
28002816 return newobj_slowpath (klass , flags , objspace , gc_cache , FALSE, heap_idx );
28012817}
28022818
2819+ /* Objects larger than the largest size pool live one-per-page in their own
2820+ * page-aligned region outside heaps[] (slot_size/total_slots are unsigned short
2821+ * and the pool sweeper strides by slot_size, so a >64 KB slot cannot live
2822+ * there). The region is registered in heap_pages.sorted so pointer->object
2823+ * recovery works, but is never linked into a heap's page list. It is reclaimed
2824+ * by the major-GC-only gc_sweep_large_objects pass. */
2825+ static rb_heap_t rb_gc_large_heap ;
2826+
2827+ static VALUE
2828+ newobj_large (rb_objspace_t * objspace , VALUE klass , VALUE flags , int wb_protected , size_t alloc_size , size_t * actual_alloc_size )
2829+ {
2830+ size_t region_size = (size_t )roomof (sizeof (struct heap_page_header ) + alloc_size + RVALUE_OVERHEAD , HEAP_PAGE_SIZE ) * HEAP_PAGE_SIZE ;
2831+
2832+ char * region = gc_aligned_malloc (HEAP_PAGE_ALIGN , region_size );
2833+ if (region == NULL ) rb_memerror ();
2834+
2835+ struct heap_page * page = calloc1 (sizeof (struct heap_page ));
2836+ if (page == NULL ) {
2837+ gc_aligned_free (region , region_size );
2838+ rb_memerror ();
2839+ }
2840+
2841+ uintptr_t obj = (uintptr_t )region + sizeof (struct heap_page_header );
2842+ ((struct heap_page_body * )region )-> header .page = page ;
2843+
2844+ page -> body = (struct heap_page_body * )region ;
2845+ page -> start = obj ;
2846+ page -> total_slots = 1 ;
2847+ page -> slot_size = 1 ; /* literally nonzero: passes the slot_size != 0 asserts */
2848+ page -> slot_size_reciprocal = 0 ;
2849+ page -> flags .large = 1 ;
2850+ page -> large_region_size = region_size ;
2851+ page -> heap = & rb_gc_large_heap ;
2852+
2853+ unsigned int lev = RB_GC_CR_LOCK ();
2854+ {
2855+ size_t lo = 0 , hi = rb_darray_size (objspace -> heap_pages .sorted );
2856+ while (lo < hi ) {
2857+ size_t mid = (lo + hi ) / 2 ;
2858+ struct heap_page * mid_page = rb_darray_get (objspace -> heap_pages .sorted , mid );
2859+ if ((uintptr_t )mid_page -> start < obj ) lo = mid + 1 ;
2860+ else hi = mid ;
2861+ }
2862+ rb_darray_insert_without_gc (& objspace -> heap_pages .sorted , lo , page );
2863+
2864+ if (heap_pages_lomem == 0 || heap_pages_lomem > obj ) heap_pages_lomem = obj ;
2865+ if (heap_pages_himem < (uintptr_t )region + region_size ) heap_pages_himem = (uintptr_t )region + region_size ;
2866+
2867+ objspace -> heap_pages .large_pages ++ ;
2868+
2869+ newobj_init (klass , flags , wb_protected , objspace , (VALUE )obj );
2870+
2871+ /* Protect a large object born mid-incremental-major from that cycle's
2872+ * sweep: the bit survives until the next full-mark start clears it. */
2873+ MARK_IN_BITMAP (GET_HEAP_MARK_BITS ((VALUE )obj ), (VALUE )obj );
2874+ }
2875+ RB_GC_CR_UNLOCK (lev );
2876+
2877+ /* No malloc backs large buffers, so without this nothing feeds the
2878+ * major-GC scheduler from large-object traffic and a large-allocation
2879+ * loop would OOM. Account the region as oldmalloc pressure. */
2880+ rb_gc_impl_adjust_memory_usage (objspace , (ssize_t )region_size );
2881+
2882+ * actual_alloc_size = region_size - sizeof (struct heap_page_header ) - RVALUE_OVERHEAD ;
2883+ return (VALUE )obj ;
2884+ }
2885+
28032886VALUE
28042887rb_gc_impl_new_obj (void * objspace_ptr , void * cache_ptr , VALUE klass , VALUE flags , bool wb_protected , size_t alloc_size , size_t * actual_alloc_size )
28052888{
@@ -2815,6 +2898,10 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags
28152898 }
28162899 }
28172900
2901+ if (RB_UNLIKELY (alloc_size > rb_gc_impl_max_allocation_size ())) {
2902+ return newobj_large (objspace , klass , flags , wb_protected , alloc_size , actual_alloc_size );
2903+ }
2904+
28182905 size_t heap_idx = heap_idx_for_size (alloc_size );
28192906 * actual_alloc_size = heap_slot_size ((unsigned char )heap_idx );
28202907
@@ -2894,6 +2981,9 @@ is_pointer_to_heap(rb_objspace_t *objspace, const void *ptr)
28942981 if (heap_page_in_global_empty_pages_pool (objspace , page )) {
28952982 return FALSE;
28962983 }
2984+ else if (page -> flags .large ) {
2985+ return p == page -> start ;
2986+ }
28972987 else {
28982988 if (p < page -> start ) return FALSE;
28992989 if (p >= page -> start + (page -> total_slots * page -> slot_size )) return FALSE;
@@ -3437,6 +3527,7 @@ rb_gc_impl_each_object(void *objspace_ptr, void (*func)(VALUE obj, void *data),
34373527
34383528 for (size_t i = 0 ; i < rb_darray_size (objspace -> heap_pages .sorted ); i ++ ) {
34393529 struct heap_page * page = rb_darray_get (objspace -> heap_pages .sorted , i );
3530+ if (page -> flags .large ) continue ;
34403531 short stride = page -> slot_size ;
34413532
34423533 uintptr_t p = (uintptr_t )page -> start ;
@@ -4232,6 +4323,29 @@ gc_sweep_freeobj_hooks(rb_objspace_t *objspace)
42324323 }
42334324}
42344325
4326+ /* Reclaim large objects. Runs once at the start of a major sweep (never on a
4327+ * minor: a large buffer owned by an OLD string is not re-marked by a minor and
4328+ * must not be freed). An unmarked large object is dead; free its region and
4329+ * drop it from heap_pages.sorted. Marked ones keep their bit until the next
4330+ * full-mark start clears it. */
4331+ static void
4332+ gc_sweep_large_objects (rb_objspace_t * objspace )
4333+ {
4334+ size_t i , j ;
4335+ for (i = j = 0 ; i < rb_darray_size (objspace -> heap_pages .sorted ); i ++ ) {
4336+ struct heap_page * page = rb_darray_get (objspace -> heap_pages .sorted , i );
4337+ if (page -> flags .large && !MARKED_IN_BITMAP (page -> mark_bits , page -> start )) {
4338+ rb_gc_impl_adjust_memory_usage (objspace , - (ssize_t )page -> large_region_size );
4339+ heap_page_free (objspace , page );
4340+ }
4341+ else {
4342+ rb_darray_set (objspace -> heap_pages .sorted , j , page );
4343+ j ++ ;
4344+ }
4345+ }
4346+ rb_darray_pop (objspace -> heap_pages .sorted , i - j );
4347+ }
4348+
42354349static void
42364350gc_sweep_start (rb_objspace_t * objspace )
42374351{
@@ -4263,6 +4377,10 @@ gc_sweep_start(rb_objspace_t *objspace)
42634377 }
42644378 }
42654379
4380+ if (is_full_marking (objspace )) {
4381+ gc_sweep_large_objects (objspace );
4382+ }
4383+
42664384 rb_gc_ractor_newobj_cache_foreach (gc_ractor_newobj_cache_clear , objspace );
42674385}
42684386
@@ -5699,6 +5817,9 @@ gc_verify_internal_consistency_(rb_objspace_t *objspace)
56995817 /* check relations */
57005818 for (size_t i = 0 ; i < rb_darray_size (objspace -> heap_pages .sorted ); i ++ ) {
57015819 struct heap_page * page = rb_darray_get (objspace -> heap_pages .sorted , i );
5820+ /* Large objects live outside heaps[] and are not counted in
5821+ * objspace_live_slots; keep them out of the reconciliation. */
5822+ if (page -> flags .large ) continue ;
57025823 short slot_size = page -> slot_size ;
57035824
57045825 uintptr_t start = (uintptr_t )page -> start ;
@@ -6306,6 +6427,18 @@ gc_marks_start(rb_objspace_t *objspace, int full_mark)
63066427 }
63076428 }
63086429 }
6430+
6431+ /* Large pages are not in any heap's page list, so the loop above skips
6432+ * them. Clear their mark bit here; a live owner re-marks the buffer
6433+ * during this major and gc_sweep_large_objects frees the rest. */
6434+ for (size_t i = 0 ; i < rb_darray_size (objspace -> heap_pages .sorted ); i ++ ) {
6435+ struct heap_page * page = rb_darray_get (objspace -> heap_pages .sorted , i );
6436+ if (page -> flags .large ) {
6437+ memset (& page -> mark_bits [0 ], 0 , HEAP_PAGE_BITMAP_SIZE );
6438+ memset (& page -> marking_bits [0 ], 0 , HEAP_PAGE_BITMAP_SIZE );
6439+ memset (& page -> pinned_bits [0 ], 0 , HEAP_PAGE_BITMAP_SIZE );
6440+ }
6441+ }
63096442 }
63106443 else {
63116444 objspace -> flags .during_minor_gc = TRUE;
@@ -8189,7 +8322,7 @@ rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym)
81898322 }
81908323
81918324 /* implementation dependent counters (small / fixnum-safe) */
8192- SET (heap_allocated_pages , rb_darray_size (objspace -> heap_pages .sorted ));
8325+ SET (heap_allocated_pages , rb_darray_size (objspace -> heap_pages .sorted ) - objspace -> heap_pages . large_pages );
81938326 SET (heap_empty_pages , objspace -> empty_pages_count )
81948327 SET (heap_allocatable_bytes , objspace -> heap_pages .allocatable_bytes );
81958328 SET (heap_eden_pages , heap_eden_total_pages (objspace ));
@@ -9917,8 +10050,7 @@ rb_gc_verify_internal_consistency(void)
991710050static VALUE
991810051gc_verify_internal_consistency_m (VALUE dummy )
991910052{
9920- rb_gc_verify_internal_consistency ();
9921- return Qnil ;
10053+ return Qnil ;
992210054}
992310055
992410056#if GC_CAN_COMPILE_COMPACTION
0 commit comments