зеркало из https://github.com/github/ruby.git
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>
This commit is contained in:
Родитель
b361a800c2
Коммит
6816e8efcf
|
@ -57,6 +57,12 @@ rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin
|
|||
|
||||
#endif
|
||||
|
||||
void
|
||||
rb_free_loaded_builtin_table(void)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
Init_builtin(void)
|
||||
{
|
||||
|
|
|
@ -18700,6 +18700,7 @@ vm.$(OBJEXT): $(top_srcdir)/internal/compar.h
|
|||
vm.$(OBJEXT): $(top_srcdir)/internal/compile.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/cont.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/encoding.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/error.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/eval.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
|
||||
|
@ -18721,6 +18722,7 @@ vm.$(OBJEXT): $(top_srcdir)/internal/string.h
|
|||
vm.$(OBJEXT): $(top_srcdir)/internal/struct.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/thread.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/transcode.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/warnings.h
|
||||
|
|
6
cont.c
6
cont.c
|
@ -275,6 +275,12 @@ struct rb_fiber_struct {
|
|||
|
||||
static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
|
||||
|
||||
void
|
||||
rb_free_shared_fiber_pool(void)
|
||||
{
|
||||
xfree(shared_fiber_pool.allocations);
|
||||
}
|
||||
|
||||
static ID fiber_initialize_keywords[3] = {0};
|
||||
|
||||
/*
|
||||
|
|
18
encoding.c
18
encoding.c
|
@ -71,6 +71,24 @@ static struct enc_table {
|
|||
st_table *names;
|
||||
} global_enc_table;
|
||||
|
||||
static int
|
||||
enc_names_free_i(st_data_t name, st_data_t idx, st_data_t args)
|
||||
{
|
||||
ruby_xfree((void *)name);
|
||||
return ST_DELETE;
|
||||
}
|
||||
|
||||
void
|
||||
rb_free_global_enc_table(void)
|
||||
{
|
||||
for (size_t i = 0; i < ENCODING_LIST_CAPA; i++) {
|
||||
xfree((void *)global_enc_table.list[i].enc);
|
||||
}
|
||||
|
||||
st_foreach(global_enc_table.names, enc_names_free_i, (st_data_t)0);
|
||||
st_free_table(global_enc_table.names);
|
||||
}
|
||||
|
||||
static rb_encoding *global_enc_ascii,
|
||||
*global_enc_utf_8,
|
||||
*global_enc_us_ascii;
|
||||
|
|
8
error.c
8
error.c
|
@ -2693,6 +2693,14 @@ syntax_error_with_path(VALUE exc, VALUE path, VALUE *mesg, rb_encoding *enc)
|
|||
|
||||
static st_table *syserr_tbl;
|
||||
|
||||
void
|
||||
rb_free_warning(void)
|
||||
{
|
||||
st_free_table(warning_categories.id2enum);
|
||||
st_free_table(warning_categories.enum2id);
|
||||
st_free_table(syserr_tbl);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
set_syserr(int n, const char *name)
|
||||
{
|
||||
|
|
36
gc.c
36
gc.c
|
@ -4582,6 +4582,35 @@ force_chain_object(st_data_t key, st_data_t val, st_data_t arg)
|
|||
|
||||
bool rb_obj_is_main_ractor(VALUE gv);
|
||||
|
||||
void
|
||||
rb_objspace_free_objects(rb_objspace_t *objspace)
|
||||
{
|
||||
for (size_t i = 0; i < heap_allocated_pages; i++) {
|
||||
struct heap_page *page = heap_pages_sorted[i];
|
||||
short stride = page->slot_size;
|
||||
|
||||
uintptr_t p = (uintptr_t)page->start;
|
||||
uintptr_t pend = p + page->total_slots * stride;
|
||||
for (; p < pend; p += stride) {
|
||||
VALUE vp = (VALUE)p;
|
||||
switch (BUILTIN_TYPE(vp)) {
|
||||
case T_DATA: {
|
||||
if (rb_obj_is_mutex(vp) || rb_obj_is_thread(vp)) {
|
||||
rb_data_free(objspace, vp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case T_ARRAY:
|
||||
obj_free(objspace, vp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
rb_objspace_call_finalizer(rb_objspace_t *objspace)
|
||||
{
|
||||
|
@ -4648,7 +4677,14 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace)
|
|||
make_io_zombie(objspace, vp);
|
||||
}
|
||||
break;
|
||||
case T_SYMBOL:
|
||||
case T_ARRAY:
|
||||
case T_NONE:
|
||||
break;
|
||||
default:
|
||||
if (rb_free_on_exit) {
|
||||
obj_free(objspace, vp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (poisoned) {
|
||||
|
|
|
@ -22,6 +22,9 @@ void rb_jit_cont_init(void);
|
|||
void rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data);
|
||||
void rb_jit_cont_finish(void);
|
||||
|
||||
/* vm.c */
|
||||
void rb_free_shared_fiber_pool(void);
|
||||
|
||||
// Copy locals from the current execution to the specified fiber.
|
||||
VALUE rb_fiber_inherit_storage(struct rb_execution_context_struct *ec, struct rb_fiber_struct *fiber);
|
||||
|
||||
|
|
|
@ -29,4 +29,7 @@ void rb_enc_set_base(const char *name, const char *orig);
|
|||
int rb_enc_set_dummy(int index);
|
||||
PUREFUNC(int rb_data_is_encoding(VALUE obj));
|
||||
|
||||
/* vm.c */
|
||||
void rb_free_global_enc_table(void);
|
||||
|
||||
#endif /* INTERNAL_ENCODING_H */
|
||||
|
|
|
@ -169,6 +169,9 @@ VALUE rb_syserr_new_path_in(const char *func_name, int n, VALUE path);
|
|||
#endif
|
||||
RUBY_SYMBOL_EXPORT_END
|
||||
|
||||
/* vm.c */
|
||||
void rb_free_warning(void);
|
||||
|
||||
static inline void
|
||||
rb_raise_cstr_i(VALUE etype, VALUE mesg)
|
||||
{
|
||||
|
|
|
@ -12,5 +12,6 @@
|
|||
|
||||
/* random.c */
|
||||
int ruby_fill_random_bytes(void *, size_t, int);
|
||||
void rb_free_default_rand_key(void);
|
||||
|
||||
#endif /* INTERNAL_RANDOM_H */
|
||||
|
|
|
@ -32,6 +32,9 @@ ID rb_make_temporary_id(size_t n);
|
|||
void rb_gc_free_dsymbol(VALUE);
|
||||
int rb_static_id_valid_p(ID id);
|
||||
|
||||
/* vm.c */
|
||||
void rb_free_static_symid_str(void);
|
||||
|
||||
#if __has_builtin(__builtin_constant_p)
|
||||
#define rb_sym_intern_ascii_cstr(ptr) \
|
||||
(__builtin_constant_p(ptr) ? \
|
||||
|
|
|
@ -17,4 +17,7 @@
|
|||
extern VALUE rb_cEncodingConverter;
|
||||
size_t rb_econv_memsize(rb_econv_t *);
|
||||
|
||||
/* vm.c */
|
||||
void rb_free_transcoder_table(void);
|
||||
|
||||
#endif /* INTERNAL_TRANSCODE_H */
|
||||
|
|
|
@ -83,6 +83,11 @@ void rb_check_stack_overflow(void);
|
|||
extern uint64_t rb_vm_insns_count;
|
||||
#endif
|
||||
|
||||
extern bool rb_free_on_exit;
|
||||
|
||||
/* miniinit.c and builtin.c */
|
||||
void rb_free_loaded_builtin_table(void);
|
||||
|
||||
/* vm_insnhelper.c */
|
||||
VALUE rb_equal_opt(VALUE obj1, VALUE obj2);
|
||||
VALUE rb_eql_opt(VALUE obj1, VALUE obj2);
|
||||
|
|
6
iseq.c
6
iseq.c
|
@ -3422,6 +3422,12 @@ typedef struct insn_data_struct {
|
|||
} insn_data_t;
|
||||
static insn_data_t insn_data[VM_INSTRUCTION_SIZE/2];
|
||||
|
||||
void
|
||||
rb_free_encoded_insn_data(void)
|
||||
{
|
||||
st_free_table(encoded_insn_data);
|
||||
}
|
||||
|
||||
void
|
||||
rb_vm_encoded_insn_data_table_init(void)
|
||||
{
|
||||
|
|
2
iseq.h
2
iseq.h
|
@ -329,6 +329,8 @@ VALUE rb_iseq_local_variables(const rb_iseq_t *iseq);
|
|||
|
||||
attr_index_t rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq);
|
||||
|
||||
void rb_free_encoded_insn_data(void);
|
||||
|
||||
RUBY_SYMBOL_EXPORT_END
|
||||
|
||||
#endif /* RUBY_ISEQ_H */
|
||||
|
|
7
load.c
7
load.c
|
@ -360,6 +360,13 @@ loaded_features_index_clear_i(st_data_t key, st_data_t val, st_data_t arg)
|
|||
return ST_DELETE;
|
||||
}
|
||||
|
||||
void
|
||||
rb_free_loaded_features_index(rb_vm_t *vm)
|
||||
{
|
||||
st_foreach(vm->loaded_features_index, loaded_features_index_clear_i, 0);
|
||||
st_free_table(vm->loaded_features_index);
|
||||
}
|
||||
|
||||
static st_table *
|
||||
get_loaded_features_index(rb_vm_t *vm)
|
||||
{
|
||||
|
|
16
marshal.c
16
marshal.c
|
@ -2517,6 +2517,20 @@ Init_marshal(void)
|
|||
rb_define_const(rb_mMarshal, "MINOR_VERSION", INT2FIX(MARSHAL_MINOR));
|
||||
}
|
||||
|
||||
static int
|
||||
free_compat_i(st_data_t key, st_data_t value, st_data_t _)
|
||||
{
|
||||
xfree((marshal_compat_t *)value);
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
free_compat_allocator_table(void *data)
|
||||
{
|
||||
st_foreach(data, free_compat_i, 0);
|
||||
st_free_table(data);
|
||||
}
|
||||
|
||||
static st_table *
|
||||
compat_allocator_table(void)
|
||||
{
|
||||
|
@ -2525,7 +2539,7 @@ compat_allocator_table(void)
|
|||
#undef RUBY_UNTYPED_DATA_WARNING
|
||||
#define RUBY_UNTYPED_DATA_WARNING 0
|
||||
compat_allocator_tbl_wrapper =
|
||||
Data_Wrap_Struct(0, mark_marshal_compat_t, 0, compat_allocator_tbl);
|
||||
Data_Wrap_Struct(0, mark_marshal_compat_t, free_compat_allocator_table, compat_allocator_tbl);
|
||||
rb_gc_register_mark_object(compat_allocator_tbl_wrapper);
|
||||
return compat_allocator_tbl;
|
||||
}
|
||||
|
|
|
@ -49,3 +49,10 @@ Init_enc(void)
|
|||
}
|
||||
|
||||
#include "mini_builtin.c"
|
||||
|
||||
void
|
||||
rb_free_loaded_builtin_table(void)
|
||||
{
|
||||
if (loaded_builtin_table)
|
||||
st_free_table(loaded_builtin_table);
|
||||
}
|
||||
|
|
|
@ -135,6 +135,7 @@ compat_init_setproctitle(int argc, char *argv[])
|
|||
}
|
||||
|
||||
#ifndef HAVE_SETPROCTITLE
|
||||
|
||||
void
|
||||
setproctitle(const char *fmt, ...)
|
||||
{
|
||||
|
|
6
random.c
6
random.c
|
@ -157,6 +157,12 @@ rand_start(rb_random_mt_t *r)
|
|||
|
||||
static rb_ractor_local_key_t default_rand_key;
|
||||
|
||||
void
|
||||
rb_free_default_rand_key(void)
|
||||
{
|
||||
xfree(default_rand_key);
|
||||
}
|
||||
|
||||
static void
|
||||
default_rand_mark(void *ptr)
|
||||
{
|
||||
|
|
5
ruby.c
5
ruby.c
|
@ -1754,6 +1754,11 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
|
|||
"environment variables RUBY_GC_HEAP_%d_INIT_SLOTS");
|
||||
}
|
||||
|
||||
if (getenv("RUBY_FREE_ON_EXIT")) {
|
||||
rb_warn("Free on exit is experimental and may be unstable");
|
||||
rb_free_on_exit = true;
|
||||
}
|
||||
|
||||
#if USE_RJIT
|
||||
// rb_call_builtin_inits depends on RubyVM::RJIT.enabled?
|
||||
if (opt->rjit.on)
|
||||
|
|
10
symbol.c
10
symbol.c
|
@ -531,6 +531,16 @@ register_sym(rb_symbols_t *symbols, VALUE str, VALUE sym)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
rb_free_static_symid_str(void)
|
||||
{
|
||||
GLOBAL_SYMBOLS_ENTER(symbols)
|
||||
{
|
||||
st_free_table(symbols->str_sym);
|
||||
}
|
||||
GLOBAL_SYMBOLS_LEAVE();
|
||||
}
|
||||
|
||||
static void
|
||||
unregister_sym(rb_symbols_t *symbols, VALUE str, VALUE sym)
|
||||
{
|
||||
|
|
|
@ -1227,4 +1227,10 @@ class TestRubyOptions < Test::Unit::TestCase
|
|||
omit "#{IO::NULL} is not a character device" unless File.chardev?(IO::NULL)
|
||||
assert_in_out_err([IO::NULL], success: true)
|
||||
end
|
||||
|
||||
def test_free_on_exit_env_var
|
||||
env = {"RUBY_FREE_ON_EXIT"=>"1"}
|
||||
assert_ruby_status([env, "-e;"])
|
||||
assert_in_out_err([env, "-W"], "", [], /Free on exit is experimental and may be unstable/)
|
||||
end
|
||||
end
|
||||
|
|
22
transcode.c
22
transcode.c
|
@ -181,6 +181,28 @@ typedef struct {
|
|||
|
||||
static st_table *transcoder_table;
|
||||
|
||||
static int
|
||||
free_inner_transcode_i(st_data_t key, st_data_t val, st_data_t arg)
|
||||
{
|
||||
xfree((void *)val);
|
||||
return ST_DELETE;
|
||||
}
|
||||
|
||||
static int
|
||||
free_transcode_i(st_data_t key, st_data_t val, st_data_t arg)
|
||||
{
|
||||
st_foreach((void *)val, free_inner_transcode_i, 0);
|
||||
st_free_table((void *)val);
|
||||
return ST_DELETE;
|
||||
}
|
||||
|
||||
void
|
||||
rb_free_transcoder_table(void)
|
||||
{
|
||||
st_foreach(transcoder_table, free_transcode_i, 0);
|
||||
st_free_table(transcoder_table);
|
||||
}
|
||||
|
||||
static transcoder_entry_t *
|
||||
make_transcoder_entry(const char *sname, const char *dname)
|
||||
{
|
||||
|
|
27
variable.c
27
variable.c
|
@ -438,6 +438,33 @@ struct rb_global_entry {
|
|||
bool ractor_local;
|
||||
};
|
||||
|
||||
static enum rb_id_table_iterator_result
|
||||
free_global_entry_i(ID key, VALUE val, void *arg)
|
||||
{
|
||||
struct rb_global_entry *entry = (struct rb_global_entry *)val;
|
||||
if (entry->var->counter == 1) {
|
||||
ruby_xfree(entry->var);
|
||||
}
|
||||
else {
|
||||
entry->var->counter--;
|
||||
}
|
||||
ruby_xfree(entry);
|
||||
return ID_TABLE_DELETE;
|
||||
}
|
||||
|
||||
void
|
||||
rb_free_rb_global_tbl(void)
|
||||
{
|
||||
rb_id_table_foreach(rb_global_tbl, free_global_entry_i, 0);
|
||||
rb_id_table_free(rb_global_tbl);
|
||||
}
|
||||
|
||||
void
|
||||
rb_free_generic_iv_tbl_(void)
|
||||
{
|
||||
st_free_table(generic_iv_tbl_);
|
||||
}
|
||||
|
||||
static struct rb_global_entry*
|
||||
rb_find_global_entry(ID id)
|
||||
{
|
||||
|
|
|
@ -33,5 +33,7 @@ int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **);
|
|||
shape_id_t rb_generic_shape_id(VALUE obj);
|
||||
#endif
|
||||
|
||||
void rb_free_rb_global_tbl(void);
|
||||
void rb_free_generic_iv_tbl_(void);
|
||||
|
||||
#endif /* RUBY_TOPLEVEL_VARIABLE_H */
|
||||
|
|
77
vm.c
77
vm.c
|
@ -16,6 +16,7 @@
|
|||
#include "internal/compile.h"
|
||||
#include "internal/cont.h"
|
||||
#include "internal/error.h"
|
||||
#include "internal/encoding.h"
|
||||
#include "internal/eval.h"
|
||||
#include "internal/gc.h"
|
||||
#include "internal/inits.h"
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "internal/ruby_parser.h"
|
||||
#include "internal/symbol.h"
|
||||
#include "internal/thread.h"
|
||||
#include "internal/transcode.h"
|
||||
#include "internal/vm.h"
|
||||
#include "internal/sanitizers.h"
|
||||
#include "internal/variable.h"
|
||||
|
@ -2055,6 +2057,13 @@ short ruby_vm_redefined_flag[BOP_LAST_];
|
|||
static st_table *vm_opt_method_def_table = 0;
|
||||
static st_table *vm_opt_mid_table = 0;
|
||||
|
||||
void
|
||||
rb_free_vm_opt_tables(void)
|
||||
{
|
||||
st_free_table(vm_opt_method_def_table);
|
||||
st_free_table(vm_opt_mid_table);
|
||||
}
|
||||
|
||||
static int
|
||||
vm_redefinition_check_flag(VALUE klass)
|
||||
{
|
||||
|
@ -2973,6 +2982,9 @@ free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg)
|
|||
return ST_DELETE;
|
||||
}
|
||||
|
||||
void rb_free_loaded_features_index(rb_vm_t *vm);
|
||||
void rb_objspace_free_objects(void *objspace);
|
||||
|
||||
int
|
||||
ruby_vm_destruct(rb_vm_t *vm)
|
||||
{
|
||||
|
@ -2980,13 +2992,57 @@ ruby_vm_destruct(rb_vm_t *vm)
|
|||
|
||||
if (vm) {
|
||||
rb_thread_t *th = vm->ractor.main_thread;
|
||||
struct rb_objspace *objspace = vm->objspace;
|
||||
vm->ractor.main_thread = NULL;
|
||||
VALUE *stack = th->ec->vm_stack;
|
||||
if (rb_free_on_exit) {
|
||||
rb_free_default_rand_key();
|
||||
rb_free_encoded_insn_data();
|
||||
rb_free_global_enc_table();
|
||||
rb_free_loaded_builtin_table();
|
||||
|
||||
if (th) {
|
||||
rb_fiber_reset_root_local_storage(th);
|
||||
thread_free(th);
|
||||
rb_free_shared_fiber_pool();
|
||||
rb_free_static_symid_str();
|
||||
rb_free_transcoder_table();
|
||||
rb_free_vm_opt_tables();
|
||||
rb_free_warning();
|
||||
rb_free_rb_global_tbl();
|
||||
rb_free_loaded_features_index(vm);
|
||||
rb_ractor_t *r = vm->ractor.main_ractor;
|
||||
xfree(r->sync.recv_queue.baskets);
|
||||
xfree(r->sync.takers_queue.baskets);
|
||||
|
||||
rb_id_table_free(vm->negative_cme_table);
|
||||
st_free_table(vm->overloaded_cme_table);
|
||||
|
||||
rb_id_table_free(RCLASS(rb_mRubyVMFrozenCore)->m_tbl);
|
||||
|
||||
rb_shape_t *cursor = rb_shape_get_root_shape();
|
||||
rb_shape_t *end = rb_shape_get_shape_by_id(GET_SHAPE_TREE()->next_shape_id);
|
||||
while (cursor < end) {
|
||||
// 0x1 == SINGLE_CHILD_P
|
||||
if (cursor->edges && !(((uintptr_t)cursor->edges) & 0x1))
|
||||
rb_id_table_free(cursor->edges);
|
||||
cursor += 1;
|
||||
}
|
||||
|
||||
xfree(GET_SHAPE_TREE());
|
||||
|
||||
st_free_table(vm->static_ext_inits);
|
||||
st_free_table(vm->ensure_rollback_table);
|
||||
|
||||
ruby_xfree(vm->postponed_job_buffer);
|
||||
st_free_table(vm->defined_module_hash);
|
||||
|
||||
rb_id_table_free(vm->constant_cache);
|
||||
}
|
||||
else {
|
||||
if (th) {
|
||||
rb_fiber_reset_root_local_storage(th);
|
||||
thread_free(th);
|
||||
}
|
||||
}
|
||||
|
||||
struct rb_objspace *objspace = vm->objspace;
|
||||
|
||||
rb_vm_living_threads_init(vm);
|
||||
ruby_vm_run_at_exit_hooks(vm);
|
||||
if (vm->loading_table) {
|
||||
|
@ -3000,6 +3056,15 @@ ruby_vm_destruct(rb_vm_t *vm)
|
|||
}
|
||||
RB_ALTSTACK_FREE(vm->main_altstack);
|
||||
if (objspace) {
|
||||
if (rb_free_on_exit) {
|
||||
rb_objspace_free_objects(objspace);
|
||||
rb_free_generic_iv_tbl_();
|
||||
if (th) {
|
||||
xfree(th->ractor);
|
||||
xfree(stack);
|
||||
ruby_mimfree(th);
|
||||
}
|
||||
}
|
||||
rb_objspace_free(objspace);
|
||||
}
|
||||
rb_native_mutex_destroy(&vm->workqueue_lock);
|
||||
|
@ -4198,6 +4263,8 @@ rb_ruby_debug_ptr(void)
|
|||
return &cr->debug;
|
||||
}
|
||||
|
||||
bool rb_free_on_exit = false;
|
||||
|
||||
/* iseq.c */
|
||||
VALUE rb_insn_operand_intern(const rb_iseq_t *iseq,
|
||||
VALUE insn, int op_no, VALUE op,
|
||||
|
|
Загрузка…
Ссылка в новой задаче