Skip to content

Commit 6816e8e

Browse files
HParkernobupeterzhu2118
committed
Free everything at shutdown
when the RUBY_FREE_ON_SHUTDOWN environment variable is set, manually free memory at shutdown. Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org> Co-authored-by: Peter Zhu <peter@peterzhu.ca>
1 parent b361a80 commit 6816e8e

27 files changed

+285
-6
lines changed

builtin.c

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin
5757

5858
#endif
5959

60+
void
61+
rb_free_loaded_builtin_table(void)
62+
{
63+
// do nothing
64+
}
65+
6066
void
6167
Init_builtin(void)
6268
{

common.mk

+2
Original file line numberDiff line numberDiff line change
@@ -18700,6 +18700,7 @@ vm.$(OBJEXT): $(top_srcdir)/internal/compar.h
1870018700
vm.$(OBJEXT): $(top_srcdir)/internal/compile.h
1870118701
vm.$(OBJEXT): $(top_srcdir)/internal/compilers.h
1870218702
vm.$(OBJEXT): $(top_srcdir)/internal/cont.h
18703+
vm.$(OBJEXT): $(top_srcdir)/internal/encoding.h
1870318704
vm.$(OBJEXT): $(top_srcdir)/internal/error.h
1870418705
vm.$(OBJEXT): $(top_srcdir)/internal/eval.h
1870518706
vm.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
@@ -18721,6 +18722,7 @@ vm.$(OBJEXT): $(top_srcdir)/internal/string.h
1872118722
vm.$(OBJEXT): $(top_srcdir)/internal/struct.h
1872218723
vm.$(OBJEXT): $(top_srcdir)/internal/symbol.h
1872318724
vm.$(OBJEXT): $(top_srcdir)/internal/thread.h
18725+
vm.$(OBJEXT): $(top_srcdir)/internal/transcode.h
1872418726
vm.$(OBJEXT): $(top_srcdir)/internal/variable.h
1872518727
vm.$(OBJEXT): $(top_srcdir)/internal/vm.h
1872618728
vm.$(OBJEXT): $(top_srcdir)/internal/warnings.h

cont.c

+6
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,12 @@ struct rb_fiber_struct {
275275

276276
static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
277277

278+
void
279+
rb_free_shared_fiber_pool(void)
280+
{
281+
xfree(shared_fiber_pool.allocations);
282+
}
283+
278284
static ID fiber_initialize_keywords[3] = {0};
279285

280286
/*

encoding.c

+18
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,24 @@ static struct enc_table {
7171
st_table *names;
7272
} global_enc_table;
7373

74+
static int
75+
enc_names_free_i(st_data_t name, st_data_t idx, st_data_t args)
76+
{
77+
ruby_xfree((void *)name);
78+
return ST_DELETE;
79+
}
80+
81+
void
82+
rb_free_global_enc_table(void)
83+
{
84+
for (size_t i = 0; i < ENCODING_LIST_CAPA; i++) {
85+
xfree((void *)global_enc_table.list[i].enc);
86+
}
87+
88+
st_foreach(global_enc_table.names, enc_names_free_i, (st_data_t)0);
89+
st_free_table(global_enc_table.names);
90+
}
91+
7492
static rb_encoding *global_enc_ascii,
7593
*global_enc_utf_8,
7694
*global_enc_us_ascii;

error.c

+8
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,14 @@ syntax_error_with_path(VALUE exc, VALUE path, VALUE *mesg, rb_encoding *enc)
26932693

26942694
static st_table *syserr_tbl;
26952695

2696+
void
2697+
rb_free_warning(void)
2698+
{
2699+
st_free_table(warning_categories.id2enum);
2700+
st_free_table(warning_categories.enum2id);
2701+
st_free_table(syserr_tbl);
2702+
}
2703+
26962704
static VALUE
26972705
set_syserr(int n, const char *name)
26982706
{

gc.c

+36
Original file line numberDiff line numberDiff line change
@@ -4582,6 +4582,35 @@ force_chain_object(st_data_t key, st_data_t val, st_data_t arg)
45824582

45834583
bool rb_obj_is_main_ractor(VALUE gv);
45844584

4585+
void
4586+
rb_objspace_free_objects(rb_objspace_t *objspace)
4587+
{
4588+
for (size_t i = 0; i < heap_allocated_pages; i++) {
4589+
struct heap_page *page = heap_pages_sorted[i];
4590+
short stride = page->slot_size;
4591+
4592+
uintptr_t p = (uintptr_t)page->start;
4593+
uintptr_t pend = p + page->total_slots * stride;
4594+
for (; p < pend; p += stride) {
4595+
VALUE vp = (VALUE)p;
4596+
switch (BUILTIN_TYPE(vp)) {
4597+
case T_DATA: {
4598+
if (rb_obj_is_mutex(vp) || rb_obj_is_thread(vp)) {
4599+
rb_data_free(objspace, vp);
4600+
}
4601+
break;
4602+
}
4603+
case T_ARRAY:
4604+
obj_free(objspace, vp);
4605+
break;
4606+
default:
4607+
break;
4608+
}
4609+
}
4610+
}
4611+
}
4612+
4613+
45854614
void
45864615
rb_objspace_call_finalizer(rb_objspace_t *objspace)
45874616
{
@@ -4648,7 +4677,14 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
46484677
make_io_zombie(objspace, vp);
46494678
}
46504679
break;
4680+
case T_SYMBOL:
4681+
case T_ARRAY:
4682+
case T_NONE:
4683+
break;
46514684
default:
4685+
if (rb_free_on_exit) {
4686+
obj_free(objspace, vp);
4687+
}
46524688
break;
46534689
}
46544690
if (poisoned) {

internal/cont.h

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ void rb_jit_cont_init(void);
2222
void rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data);
2323
void rb_jit_cont_finish(void);
2424

25+
/* vm.c */
26+
void rb_free_shared_fiber_pool(void);
27+
2528
// Copy locals from the current execution to the specified fiber.
2629
VALUE rb_fiber_inherit_storage(struct rb_execution_context_struct *ec, struct rb_fiber_struct *fiber);
2730

internal/encoding.h

+3
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,7 @@ void rb_enc_set_base(const char *name, const char *orig);
2929
int rb_enc_set_dummy(int index);
3030
PUREFUNC(int rb_data_is_encoding(VALUE obj));
3131

32+
/* vm.c */
33+
void rb_free_global_enc_table(void);
34+
3235
#endif /* INTERNAL_ENCODING_H */

internal/error.h

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ VALUE rb_syserr_new_path_in(const char *func_name, int n, VALUE path);
169169
#endif
170170
RUBY_SYMBOL_EXPORT_END
171171

172+
/* vm.c */
173+
void rb_free_warning(void);
174+
172175
static inline void
173176
rb_raise_cstr_i(VALUE etype, VALUE mesg)
174177
{

internal/random.h

+1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212

1313
/* random.c */
1414
int ruby_fill_random_bytes(void *, size_t, int);
15+
void rb_free_default_rand_key(void);
1516

1617
#endif /* INTERNAL_RANDOM_H */

internal/symbol.h

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ ID rb_make_temporary_id(size_t n);
3232
void rb_gc_free_dsymbol(VALUE);
3333
int rb_static_id_valid_p(ID id);
3434

35+
/* vm.c */
36+
void rb_free_static_symid_str(void);
37+
3538
#if __has_builtin(__builtin_constant_p)
3639
#define rb_sym_intern_ascii_cstr(ptr) \
3740
(__builtin_constant_p(ptr) ? \

internal/transcode.h

+3
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@
1717
extern VALUE rb_cEncodingConverter;
1818
size_t rb_econv_memsize(rb_econv_t *);
1919

20+
/* vm.c */
21+
void rb_free_transcoder_table(void);
22+
2023
#endif /* INTERNAL_TRANSCODE_H */

internal/vm.h

+5
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ void rb_check_stack_overflow(void);
8383
extern uint64_t rb_vm_insns_count;
8484
#endif
8585

86+
extern bool rb_free_on_exit;
87+
88+
/* miniinit.c and builtin.c */
89+
void rb_free_loaded_builtin_table(void);
90+
8691
/* vm_insnhelper.c */
8792
VALUE rb_equal_opt(VALUE obj1, VALUE obj2);
8893
VALUE rb_eql_opt(VALUE obj1, VALUE obj2);

iseq.c

+6
Original file line numberDiff line numberDiff line change
@@ -3422,6 +3422,12 @@ typedef struct insn_data_struct {
34223422
} insn_data_t;
34233423
static insn_data_t insn_data[VM_INSTRUCTION_SIZE/2];
34243424

3425+
void
3426+
rb_free_encoded_insn_data(void)
3427+
{
3428+
st_free_table(encoded_insn_data);
3429+
}
3430+
34253431
void
34263432
rb_vm_encoded_insn_data_table_init(void)
34273433
{

iseq.h

+2
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ VALUE rb_iseq_local_variables(const rb_iseq_t *iseq);
329329

330330
attr_index_t rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq);
331331

332+
void rb_free_encoded_insn_data(void);
333+
332334
RUBY_SYMBOL_EXPORT_END
333335

334336
#endif /* RUBY_ISEQ_H */

load.c

+7
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,13 @@ loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
360360
return ST_DELETE;
361361
}
362362

363+
void
364+
rb_free_loaded_features_index(rb_vm_t *vm)
365+
{
366+
st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
367+
st_free_table(vm->loaded_features_index);
368+
}
369+
363370
static st_table *
364371
get_loaded_features_index(rb_vm_t *vm)
365372
{

marshal.c

+15-1
Original file line numberDiff line numberDiff line change
@@ -2517,6 +2517,20 @@ Init_marshal(void)
25172517
rb_define_const(rb_mMarshal, "MINOR_VERSION", INT2FIX(MARSHAL_MINOR));
25182518
}
25192519

2520+
static int
2521+
free_compat_i(st_data_t key, st_data_t value, st_data_t _)
2522+
{
2523+
xfree((marshal_compat_t *)value);
2524+
return ST_CONTINUE;
2525+
}
2526+
2527+
static void
2528+
free_compat_allocator_table(void *data)
2529+
{
2530+
st_foreach(data, free_compat_i, 0);
2531+
st_free_table(data);
2532+
}
2533+
25202534
static st_table *
25212535
compat_allocator_table(void)
25222536
{
@@ -2525,7 +2539,7 @@ compat_allocator_table(void)
25252539
#undef RUBY_UNTYPED_DATA_WARNING
25262540
#define RUBY_UNTYPED_DATA_WARNING 0
25272541
compat_allocator_tbl_wrapper =
2528-
Data_Wrap_Struct(0, mark_marshal_compat_t, 0, compat_allocator_tbl);
2542+
Data_Wrap_Struct(0, mark_marshal_compat_t, free_compat_allocator_table, compat_allocator_tbl);
25292543
rb_gc_register_mark_object(compat_allocator_tbl_wrapper);
25302544
return compat_allocator_tbl;
25312545
}

miniinit.c

+7
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,10 @@ Init_enc(void)
4949
}
5050

5151
#include "mini_builtin.c"
52+
53+
void
54+
rb_free_loaded_builtin_table(void)
55+
{
56+
if (loaded_builtin_table)
57+
st_free_table(loaded_builtin_table);
58+
}

missing/setproctitle.c

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ compat_init_setproctitle(int argc, char *argv[])
135135
}
136136

137137
#ifndef HAVE_SETPROCTITLE
138+
138139
void
139140
setproctitle(const char *fmt, ...)
140141
{

random.c

+6
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ rand_start(rb_random_mt_t *r)
157157

158158
static rb_ractor_local_key_t default_rand_key;
159159

160+
void
161+
rb_free_default_rand_key(void)
162+
{
163+
xfree(default_rand_key);
164+
}
165+
160166
static void
161167
default_rand_mark(void *ptr)
162168
{

ruby.c

+5
Original file line numberDiff line numberDiff line change
@@ -1754,6 +1754,11 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
17541754
"environment variables RUBY_GC_HEAP_%d_INIT_SLOTS");
17551755
}
17561756

1757+
if (getenv("RUBY_FREE_ON_EXIT")) {
1758+
rb_warn("Free on exit is experimental and may be unstable");
1759+
rb_free_on_exit = true;
1760+
}
1761+
17571762
#if USE_RJIT
17581763
// rb_call_builtin_inits depends on RubyVM::RJIT.enabled?
17591764
if (opt->rjit.on)

symbol.c

+10
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,16 @@ register_sym(rb_symbols_t *symbols, VALUE str, VALUE sym)
531531
}
532532
}
533533

534+
void
535+
rb_free_static_symid_str(void)
536+
{
537+
GLOBAL_SYMBOLS_ENTER(symbols)
538+
{
539+
st_free_table(symbols->str_sym);
540+
}
541+
GLOBAL_SYMBOLS_LEAVE();
542+
}
543+
534544
static void
535545
unregister_sym(rb_symbols_t *symbols, VALUE str, VALUE sym)
536546
{

test/ruby/test_rubyoptions.rb

+6
Original file line numberDiff line numberDiff line change
@@ -1227,4 +1227,10 @@ def test_null_script
12271227
omit "#{IO::NULL} is not a character device" unless File.chardev?(IO::NULL)
12281228
assert_in_out_err([IO::NULL], success: true)
12291229
end
1230+
1231+
def test_free_on_exit_env_var
1232+
env = {"RUBY_FREE_ON_EXIT"=>"1"}
1233+
assert_ruby_status([env, "-e;"])
1234+
assert_in_out_err([env, "-W"], "", [], /Free on exit is experimental and may be unstable/)
1235+
end
12301236
end

transcode.c

+22
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,28 @@ typedef struct {
181181

182182
static st_table *transcoder_table;
183183

184+
static int
185+
free_inner_transcode_i(st_data_t key, st_data_t val, st_data_t arg)
186+
{
187+
xfree((void *)val);
188+
return ST_DELETE;
189+
}
190+
191+
static int
192+
free_transcode_i(st_data_t key, st_data_t val, st_data_t arg)
193+
{
194+
st_foreach((void *)val, free_inner_transcode_i, 0);
195+
st_free_table((void *)val);
196+
return ST_DELETE;
197+
}
198+
199+
void
200+
rb_free_transcoder_table(void)
201+
{
202+
st_foreach(transcoder_table, free_transcode_i, 0);
203+
st_free_table(transcoder_table);
204+
}
205+
184206
static transcoder_entry_t *
185207
make_transcoder_entry(const char *sname, const char *dname)
186208
{

variable.c

+27
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,33 @@ struct rb_global_entry {
438438
bool ractor_local;
439439
};
440440

441+
static enum rb_id_table_iterator_result
442+
free_global_entry_i(ID key, VALUE val, void *arg)
443+
{
444+
struct rb_global_entry *entry = (struct rb_global_entry *)val;
445+
if (entry->var->counter == 1) {
446+
ruby_xfree(entry->var);
447+
}
448+
else {
449+
entry->var->counter--;
450+
}
451+
ruby_xfree(entry);
452+
return ID_TABLE_DELETE;
453+
}
454+
455+
void
456+
rb_free_rb_global_tbl(void)
457+
{
458+
rb_id_table_foreach(rb_global_tbl, free_global_entry_i, 0);
459+
rb_id_table_free(rb_global_tbl);
460+
}
461+
462+
void
463+
rb_free_generic_iv_tbl_(void)
464+
{
465+
st_free_table(generic_iv_tbl_);
466+
}
467+
441468
static struct rb_global_entry*
442469
rb_find_global_entry(ID id)
443470
{

0 commit comments

Comments
 (0)