@@ -260,6 +260,7 @@ VALUE secondary_map_mutex = Qnil;
260
260
// Lambda that will GC entries from the secondary map that are no longer present
261
261
// in the primary map.
262
262
VALUE gc_secondary_map = Qnil ;
263
+ ID length ;
263
264
264
265
extern VALUE weak_obj_cache ;
265
266
@@ -273,14 +274,30 @@ static void SecondaryMap_Init() {
273
274
" secondary.delete_if { |k, v| !weak.key?(v) }\n"
274
275
"}\n" );
275
276
secondary_map_mutex = rb_mutex_new ();
277
+ length = rb_intern ("length" );
276
278
}
277
279
280
+ // The secondary map is a regular Hash, and will never shrink on its own.
281
+ // The main object cache is a WeakMap that will automatically remove entries
282
+ // when the target object is no longer reachable, but unless we manually
283
+ // remove the corresponding entries from the secondary map, it will grow
284
+ // without bound.
285
+ //
286
+ // To avoid this unbounded growth we periodically remove entries from the
287
+ // secondary map that are no longer present in the WeakMap. The logic of
288
+ // how often to perform this GC is an artbirary tuning parameter that
289
+ // represents a straightforward CPU/memory tradeoff.
278
290
static void SecondaryMap_MaybeGC () {
279
- size_t weak_len = NUM2ULL (rb_funcall (weak_obj_cache , rb_intern ( " length" ) , 0 ));
291
+ size_t weak_len = NUM2ULL (rb_funcall (weak_obj_cache , length , 0 ));
280
292
size_t secondary_len = RHASH_SIZE (secondary_map );
281
293
size_t waste = secondary_len - weak_len ;
282
294
PBRUBY_ASSERT (secondary_len >= weak_len );
283
- if (waste > 1000 && waste > secondary_len * 0.2 ) {
295
+ // GC if we could remove at least 2000 entries or 20% of the table size
296
+ // (whichever is greater). Since the cost of the GC pass is O(N), we
297
+ // want to make sure that we condition this on overall table size, to
298
+ // avoid O(N^2) CPU costs.
299
+ size_t threshold = PBRUBY_MAX (secondary_len * 0.2 , 2000 );
300
+ if (waste > threshold ) {
284
301
rb_funcall (gc_secondary_map , rb_intern ("call" ), 2 , secondary_map , weak_obj_cache );
285
302
}
286
303
}
0 commit comments