为什么重新分配字符串不会减少Ruby中的内存使用量?

S.G*_*ami 1 ruby memory

我只是注意到,如果您不清除或替换字符串,则内存使用不会减少。

文件x.rb:

#!/usr/bin/ruby -w
raise(RuntimeError, 'A GNU/Linux or an Android system is needed') unless /linux/ === RUBY_PLATFORM.downcase

require 'objspace'
STDOUT.sync = true
GC.start(full_mark: true, immediate_sweep: true)

define_method(:show) { "System Memory Usage: #{::IO.readlines('/proc/meminfo').then { |x| [x[0], x[2]] }
                            .map { |x| x.split[1].to_f }.reduce(:-)./(1024).round(3)} MiB "\
                                "| Available: #{::IO.readlines('/proc/meminfo')[2].split[1].to_f./(1024).round(3)} MiB" }

define_method(:memsize) { |obj| ObjectSpace.memsize_of(obj).to_s.reverse.gsub(/\d{1,3}/).to_a.join(',').reverse << ' Bytes'}
Run Code Online (Sandbox Code Playgroud)

文件y.rb:

#!/usr/bin/ruby -w
fail(NoMemoryError, 'Not enough available memory') if ::IO.readlines('/proc/meminfo')[2].split[1].to_i < 600_000
require_relative(File.join(__dir__, 'x'))
puts show

a = '0' * 500_000_000
puts "Memory used by a: #{memsize(a)}"
puts show

a = ''
puts "Memory used by a: #{memsize(a)}"
puts show
Run Code Online (Sandbox Code Playgroud)

文件z.rb:

#!/usr/bin/ruby -w
fail(NoMemoryError, 'Not enough available memory') if ::IO.readlines('/proc/meminfo')[2].split[1].to_i < 600_000
require_relative(File.join(__dir__, 'x'))

puts show

a = '0' * 500_000_000
puts "Memory used by a: #{memsize(a)}"
puts show

a.clear

puts "Memory used by a: #{memsize(a)}"
puts show
Run Code Online (Sandbox Code Playgroud)

y.rb的输出:

System Memory Usage: 2316.289 MiB | Available: 1445.23 MiB
Memory used by a: 500,000,041 Bytes
System Memory Usage: 2795.504 MiB | Available: 966.016 MiB
Memory used by a: 40 Bytes
System Memory Usage: 2795.504 MiB | Available: 966.016 MiB
Run Code Online (Sandbox Code Playgroud)

z.rb的输出:

System Memory Usage: 2301.359 MiB | Available: 1460.16 MiB
Memory used by a: 500,000,041 Bytes
System Memory Usage: 2780.098 MiB | Available: 981.422 MiB
Memory used by a: 40 Bytes
System Memory Usage: 2303.387 MiB | Available: 1458.133 MiB
Run Code Online (Sandbox Code Playgroud)

现在,尽管将a分配给空字符串,问题仍然存在,运行文件y.rb会占用大约500兆的内存,直到程序退出。

z.rb清除字符串。

这也不会清除内存:

a[0..-1] = ''
Run Code Online (Sandbox Code Playgroud)

请注意,我的程序和gnome-system-monitor都同意使用系统RAM。

为什么会这样呢?当赋值运算符不起作用时,清除如何工作?

Sch*_*ern 8

a = ''a.clear做些微妙的事情。

a = ''创建一个新String对象并将其分配给a。旧String对象仍在内存中浮动,等待被垃圾回收。

2.4.4 :010 > a = 'foo'
 => "foo" 
2.4.4 :011 > a.object_id
 => 70311739468740 
2.4.4 :012 > a = ''
 => "" 
2.4.4 :013 > a.object_id
 => 70311748786840 
Run Code Online (Sandbox Code Playgroud)

注意不同的对象ID。

a.clear清除现有String对象。

2.4.4 :016 > a = 'foo'
 => "foo" 
2.4.4 :017 > a.object_id
 => 70311748749240 
2.4.4 :018 > a.clear
 => "" 
2.4.4 :019 > a.object_id
 => 70311748749240 
Run Code Online (Sandbox Code Playgroud)

注意对象ID是相同的。

专门clear调用str_discard会立即释放分配给的内存String

static inline void
str_discard(VALUE str)
{
    str_modifiable(str);
    if (!STR_EMBED_P(str) && !FL_TEST(str, STR_SHARED|STR_NOFREE)) {
    ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str));
    RSTRING(str)->as.heap.ptr = 0;
    RSTRING(str)->as.heap.len = 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种看到差异的方式...

2.4.4 :026 > a = 'foo'
 => "foo" 
2.4.4 :027 > b = a
 => "foo" 
2.4.4 :028 > a.object_id
 => 70311748602540 
2.4.4 :029 > b.object_id
 => 70311748602540
Run Code Online (Sandbox Code Playgroud)

ab指向同一基础对象。

2.4.4 :030 > a = ''
 => "" 
2.4.4 :031 > b
 => "foo" 
2.4.4 :032 > a.object_id
 => 70311748541360 
2.4.4 :033 > b.object_id
 => 70311748602540 
Run Code Online (Sandbox Code Playgroud)

在之后a = ''a指向一个新对象,而b指向原始对象。这说明了为什么a = ''无法立即释放内存,而其他可能引用了原始内存String

如果我们再次设置...

2.4.4 :034 > a = 'foo'
 => "foo" 
2.4.4 :035 > b = a
 => "foo" 
2.4.4 :036 > a.object_id
 => 70311748490260 
2.4.4 :037 > b.object_id
 => 70311748490260 
Run Code Online (Sandbox Code Playgroud)

但是这次使用a.clear...

2.4.4 :038 > a.clear
 => "" 
2.4.4 :039 > b
 => "" 
2.4.4 :040 > a.object_id
 => 70311748490260 
2.4.4 :041 > b.object_id
 => 70311748490260 
Run Code Online (Sandbox Code Playgroud)

a并且b仍然都引用同一个对象。