2015-10-03 1 views
20

Я пытаюсь понять, когда память, выделенная из кучи 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, который может дать объяснение, но меня будут интересовать объяснения других.

Освобождение памяти обратно к ядру также требует затрат. Память пользовательского пространства распределители могут удерживаться в этой памяти (в частном порядке) в надежде, что она может быть повторно использована в том же процессе и не возвращает ее ядру для использования в других процессах.

+1

Подписавшись на эту тему. Я тоже очень заинтересован в этом. – dimitarvp

+1

Фундаментальное отличие заключается в 1-м примере, когда создаются объекты * 600 * * * *, а во втором - только один. Хотя общий размер * указанных * данных одинаковый, для первого примера требуется 600 тысяч раз больше слотов для ссылок на объекты (которые, вероятно, никогда или позже не возвращаются в ОС). – joanbm

+2

Я бы предложил следующее [статья] (http://www.sitepoint.com/ruby-uses-memory/) и связал [объяснение] (http://rocket-science.ru/hacking/2013/12/17/ruby-memory-pitfalls /) из 'RVALUE'. Я не уверен, что они совершенно правильны, может знать только сам Коичи, известный как сам. Или какой-то решительно настроенный энтузиаст, анализирующий источники Ruby. – joanbm

ответ

1

@joanbm имеет очень хорошую точку здесь. Его ссылка article explains this pretty well:

GC GC Ruby постепенно выпускает память, поэтому, когда вы делаете GC на 1 большой кусок памяти, на который ссылается 1 ссылка, он выпускает все, но когда есть много ссылок, GC будет выпускать память меньшими размерами chuncks.

Несколько звонков в GC.start высвободят все больше и больше памяти в первом примере.


Вот 2 orther статьи копать глубже: