Как я могу сделать Ruby выпуском unreferenced памяти?

Рассмотрим этот фрагмент:

l = []

while 1
  l << 'a random 369-characterish string'
end
^C
# ran this for maybe 4 seconds, and it had 27 million entries in l. memory
# usage was 1.6 GB.

l = nil

# no change in memory usage

GC.start

# memory usage drops a relatively small amount, from 1.6 GB to 1.39 GB.

Я нажимаю миллионы элементов в/через структуры данных Ruby и имею некоторые серьезные проблемы с памятью. Этот пример демонстрирует, что даже в случае, когда ссылка на объект не существует, Ruby не будет пропускать [большую часть], даже после явного вызова GC.start.

Объекты, которые я использую в реальной жизни, выталкивают миллионы элементов в хэш в целом, но хеш используется как временная таблица поиска и обнуляется после завершения какого-либо цикла. Однако память из этой таблицы поиска, по-видимому, никогда не выпускается, и это замедляет мое приложение ужасно и непродолжительно, потому что GC имеет миллионы несуществующих объектов для анализа в каждом цикле. Я работаю над обходным решением с драгоценным камнем sparsehash, но это не похоже на неразрешимую проблему, которая должна затухать в Ruby runtime. Ссылки четко удалены, и объекты должны быть четко собраны и удалены. Может ли кто-нибудь помочь мне понять, почему этого не происходит?

Я попробовал l.delete_if { |x| true} по предложению пользователя в #ruby на freenode, но это было очень медленно и также никогда не вызывало заметного освобождения памяти.

Использование ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux].

EDIT:

Для сравнения, это пробег в python3:

l = []

while 1:
    l.append('a random 369-characterish string')
^C
# 31,216,082 elements; 246M memory usage.

l = []
# memory usage drops to 8K (0% of system total)

Тест на python2 показывает почти идентичные результаты.

Я не уверен, достаточно ли этого, чтобы рассмотреть этот недостаток реализации в МРТ или если он просто записал различные подходы к GC. В любом случае, похоже, что Python лучше подходит для использования случаев, которые собирают миллионы элементов в целом через структуры данных и периодически обнуляют структуры (как это можно сделать для временной таблицы поиска).

Кажется, что это должно быть просто.:\

Ответ 1

Вид хаки, но вы можете попробовать fork выполнить операцию как отдельный процесс. Процесс будет выполняться в области общей памяти; когда он завершается, память будет освобождена.

Ruby, возможно, не освобождает память обратно в ядро, как отметил @Sergio Tulentsev в комментариях.

Этот разговор по списку рассылки Ruby/Unix подробно описывает это: Избегайте системных вызовов

Кроме того, это сообщение в блоге описывает forking как решение для управления памятью в Rails: Сохранение памяти в Ruby on Rails с помощью fork() и copy-on-write. Хотя, я не думаю, что Ruby будет поддерживать copy-on-write, пока не появится Ruby 2.