From: "peterzhu2118 (Peter Zhu)" Date: 2021-09-07T20:43:38+00:00 Subject: [ruby-core:105172] [Ruby master Bug#18154] String#initialize leaks memory for STR_NOFREE strings Issue #18154 has been updated by peterzhu2118 (Peter Zhu). Indeed, nobody should ever call #initialize on any object more than once. However, making it illegal for calling #initialize multiple times will likely be a breaking change as it's probably a feature used out in the wild. ---------------------------------------- Bug #18154: String#initialize leaks memory for STR_NOFREE strings https://bugs.ruby-lang.org/issues/18154#change-93576 * Author: peterzhu2118 (Peter Zhu) * Status: Open * Priority: Normal * Backport: 2.6: REQUIRED, 2.7: REQUIRED, 3.0: REQUIRED ---------------------------------------- # GitHub PR: https://github.com/ruby/ruby/pull/4814 There is a memory leak in calling the constructor on a string that is marked `STR_NOFREE` (e.g. a string created from a C string literal). The script below reproduces the memory leak. This is reproducible on all maintained Rubies (2.6.8, 2.7.4, 3.0.2, master) on Ubuntu 20.04. We create a string marked `STR_NOFREE` with `0.to_s`. `to_s` for Fixnum has a [special optimization](https://github.com/ruby/ruby/blob/26153667f91f0c883f6af6b61fac2c0df5312b45/numeric.c#L3393) for the value `0` (it directly converts it to a C string literal). When we call `String#initialize` with a capacity it creates a buffer using `malloc` but does not unset the `STR_NOFREE` flag. This causes the buffer to be permanently leaked. ```ruby 100.times do 1000.times do # 0.to_s is a special case that creates a string from a C string literal. # https://github.com/ruby/ruby/blob/26153667f91f0c883f6af6b61fac2c0df5312b45/numeric.c#L3393 # C string literals are always marked STR_NOFREE. str = 0.to_s # Call String#initialize again to create a buffer with a capacity of 10000 # characters. str.send(:initialize, capacity: 10000) end # Output the Resident Set Size (memory usage, in KB) of the current Ruby process. puts `ps -o rss= -p #{$$}` end ``` We can see the leak through the following graph of the Resident Set Size (RSS) comparing the branch vs. master (at commit 26153667f91f0c883f6af6b61fac2c0df5312b45). ![](https://user-images.githubusercontent.com/15860699/132392215-9686259e-8c76-4fc9-9b63-427b89f8df2c.png) -- https://bugs.ruby-lang.org/ Unsubscribe: