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

标签 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'}

文件 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

文件 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

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

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

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

z.rb 清除字符串。

这也不会清除内存:

a[0..-1] = ''

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

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

最佳答案

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 

注意不同的对象 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 

请注意对象 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;
    }
}

另一种查看差异的方法...

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

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 

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 

但是这次使用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 

ab 仍然指代同一个对象。

关于ruby - 为什么重新分配字符串不会减少 Ruby 中的内存使用量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56743116/

相关文章:

javascript - 使用nodejs在windows中读取进程的内存值

c# - 量化垃圾收集与显式内存管理的性能

ruby - ruby 数组中的空间按 %w

ruby - 如何确定在 Watir 中选择了哪种类型的 HTML 元素

ruby - 使用 Ruby 2.0.0-p0 和 Rails 3.2.13,Rails 将无法在 'too short escaped multibyte character' 的 OS X 10.8.3 上启动

ruby - 何时使用 block

c++ - Insufficient memory 错误 Opencv/c++

c - C 语言中未声明的实体如何管理内存?

ruby - Rubocop 25 行 block 大小和 RSpec 测试

java - 如何识别 Windows 中的默认 Java 堆大小