Почему переназначение строки не уменьшает использование памяти в Ruby? - PullRequest
1 голос
/ 24 июня 2019

Я только что заметил, что если вы не очистите или не замените строку, использование памяти не уменьшится.

Файл 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 согласуются с использованием оперативной памяти системы.

Почему это так?Как работает очистка, когда оператор присваивания не работает?

1 Ответ

8 голосов
/ 24 июня 2019

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 

Обратите внимание на различные идентификаторы объектов.

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 

Обратите внимание, что идентификатор объекта такой же.

В частности 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

a и b указывают на один и тот же базовый объект.

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 

a и b по-прежнему относятся к одному и тому же объекту.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...