Я пытаюсь понять, когда память, выделенная из кучи Ruby, возвращается в операционную систему. Я понимаю, что Ruby никогда не возвращает память, выделенную в кучу, но я все еще не уверен в поведении памяти кучи. то есть те объекты, которые не вписываются в 40-байтовый RVALUE.Почему эта программа Ruby не возвращает кучную память в операционную систему?
Рассмотрите следующую программу, которая выделяет некоторые большие строки, а затем заставляет основной GC.
require 'objspace'
STRING_SIZE = 250
def print_stats(msg)
puts '-------------------'
puts msg
puts '-------------------'
puts "RSS: #{`ps -eo rss,pid | grep #{Process.pid} | grep -v grep | awk '{ print $1,"KB";}'`}"
puts "HEAP SIZE: #{(GC.stat[:heap_sorted_length] * 408 * 40)/1024} KB"
puts "SIZE OF ALL OBJECTS: #{ObjectSpace.memsize_of_all/1024} KB"
end
def run
print_stats('START WORK')
@data=[]
600_000.times do
@data << " " * STRING_SIZE
end
print_stats('END WORK')
@data=nil
end
run
GC.start
print_stats('AFTER FORCED MAJOR GC')
Выполнение этой программы с использованием Ruby 2.2.3 на MRI, создает следующий результат. После принудительного крупного GC размер кучи будет таким же ожидаемым, но RSS не уменьшится значительно.
-------------------
START WORK
-------------------
RSS: 7036 KB
HEAP SIZE: 1195 KB
SIZE OF ALL OBJECTS: 3172 KB
-------------------
END WORK
-------------------
RSS: 205660 KB
HEAP SIZE: 35046 KB
SIZE OF ALL OBJECTS: 178423 KB
-------------------
AFTER FORCED MAJOR GC
-------------------
RSS: 164492 KB
HEAP SIZE: 35046 KB
SIZE OF ALL OBJECTS: 2484 KB
Сравните эти результаты со следующими результатами, когда мы выделяем один большой объект вместо многих мелких объектов.
def run
print_stats('START WORK')
@data = " " * STRING_SIZE * 600_000
print_stats('END WORK')
@data=nil
end
-------------------
START WORK
-------------------
RSS: 7072 KB
HEAP SIZE: 1195 KB
SIZE OF ALL OBJECTS: 3170 KB
-------------------
END WORK
-------------------
RSS: 153584 KB
HEAP SIZE: 1195 KB
SIZE OF ALL OBJECTS: 149064 KB
-------------------
AFTER FORCED MAJOR GC
-------------------
RSS: 7096 KB
HEAP SIZE: 1195 KB
SIZE OF ALL OBJECTS: 2483 KB
Обратите внимание на окончательное значение RSS. Кажется, мы освободили всю память, которую мы выделили для большой строки.
Я не уверен, почему второй пример освобождает память, но первый пример не работает, поскольку они оба выделяют память из кучи Ruby. Это один reference, который может дать объяснение, но меня будут интересовать объяснения других.
Освобождение памяти обратно к ядру также требует затрат. Память пользовательского пространства распределители могут удерживаться в этой памяти (в частном порядке) в надежде, что она может быть повторно использована в том же процессе и не возвращает ее ядру для использования в других процессах.
Подписавшись на эту тему. Я тоже очень заинтересован в этом. – dimitarvp
Фундаментальное отличие заключается в 1-м примере, когда создаются объекты * 600 * * * *, а во втором - только один. Хотя общий размер * указанных * данных одинаковый, для первого примера требуется 600 тысяч раз больше слотов для ссылок на объекты (которые, вероятно, никогда или позже не возвращаются в ОС). – joanbm
Я бы предложил следующее [статья] (http://www.sitepoint.com/ruby-uses-memory/) и связал [объяснение] (http://rocket-science.ru/hacking/2013/12/17/ruby-memory-pitfalls /) из 'RVALUE'. Я не уверен, что они совершенно правильны, может знать только сам Коичи, известный как сам. Или какой-то решительно настроенный энтузиаст, анализирующий источники Ruby. – joanbm