Fix memory leak of `rb_ast_t` in parser
authorNobuyoshi Nakada <[email protected]>
Wed, 1 May 2024 06:36:36 +0000 (1 15:36 +0900)
committerYuichiro Kaneko <[email protected]>
Thu, 2 May 2024 01:14:54 +0000 (2 10:14 +0900)
Do not allocate `rb_ast_t` in `ast_alloc` to avoid memory leak.

For example:

    10.times do
      100_000.times do
        eval("")
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    17568
    20960
    24096
    27808
    31008
    34160
    37312
    40464
    43568
    46816

After:

    14432
    14448
    14496
    14576
    14592
    15072
    15072
    15072
    15072
    15088

ruby_parser.c

index 17b98a3..4c981df 100644 (file)
@@ -784,13 +784,7 @@ static const rb_data_type_t ast_data_type = {
 static VALUE
 ast_alloc(void)
 {
-    rb_ast_t *ast;
-    VALUE vast = TypedData_Make_Struct(0, rb_ast_t, &ast_data_type, ast);
-#ifdef UNIVERSAL_PARSER
-    ast = (rb_ast_t *)DATA_PTR(vast);
-    ast->config = &rb_global_parser_config;
-#endif
-    return vast;
+    return TypedData_Wrap_Struct(0, &ast_data_type, NULL);
 }
 
 VALUE
@@ -1142,8 +1136,11 @@ parser_aset_script_lines_for(VALUE path, rb_parser_ary_t *lines)
 VALUE
 rb_ruby_ast_new(const NODE *const root)
 {
-    VALUE vast = ast_alloc();
-    rb_ast_t *ast = DATA_PTR(vast);
+    rb_ast_t *ast;
+    VALUE vast = TypedData_Make_Struct(0, rb_ast_t, &ast_data_type, ast);
+#ifdef UNIVERSAL_PARSER
+    ast->config = &rb_global_parser_config;
+#endif
     ast->body = (rb_ast_body_t){
         .root = root,
         .frozen_string_literal = -1,