утечка хэш-памяти в рубине после удаления ключа - PullRequest
5 голосов
/ 11 мая 2011

Helo, я не могу добиться успеха, как освободить память после удаления ключа в хеше. Когда я удаляю ключ из Hash, память не освобождается ни после вызова GC.start вручную. Это ожидаемое поведение или GC не освобождает память, когда ключи удаляются из Hash, и эти объекты где-то протекают? Как я могу удалить ключ в Hash в Ruby и выделить его также в памяти?

Пример:

irb(main):001:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 4748
irb(main):002:0> a = {}
=> {}
irb(main):003:0> 1000000.times{|i| a[i] = "test #{i}"}
=> 1000000
irb(main):004:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 140340
irb(main):005:0> 1000000.times{|i| a.delete(i)}
=> 1000000
irb(main):006:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 140364
irb(main):007:0> GC.start
=> nil
irb(main):008:0> `ps -o rss= -p #{Process.pid}`.to_i
=> 127076

PS: я использую ruby ​​1.8.7. Я пробовал также ruby ​​1.9.2, но это было не лучше.

Ответы [ 4 ]

6 голосов
/ 11 мая 2011

См. Stackoverflow: как Malloc и Free Work

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

Чтобы увидеть изменение процесса, базовые malloc и free в C-части интерпретатора Ruby должны будут вернуть хост-оператору память назад.Этого не произойдет, но на уровне Ruby объекты были помечены GC и находятся в локально сохраненном свободном списке в интерпретаторе.

3 голосов
/ 12 мая 2011

Как старший разработчик, который долгое время занимался этим на многих языках, вот мои мысли:

Хотя я думаю, что при использовании скомпилированного языка, такого как C, у вас хорошие намерения, детальное управление памятью, управляемое разработчиком, не соответствует тому, как работают такие языки, как Ruby, Python и Perl.

Языки сценариев, такие как Perl, Ruby и Python, защищают нас от забот управления памятью. Это одна из причин, по которой они нам нравятся. Если у нас будет доступная память, они будут использовать то, что им нужно, чтобы выполнить работу. Потеря контроля управления памятью является компромиссом для скорости разработки и простоты отладки; У нас его нет, и нам не нужно об этом беспокоиться. Если мне это нужно, я буду использовать C или ассемблер.

Если предположить, что это утечка памяти, я думаю, это немного наивно или самонадеянно. Утечка памяти, о которой вы упомянули, будет значительной утечкой, и с таким большим количеством приложений и сайтов на основе Ruby кто-то заметил бы это давно. Итак, в качестве проверки работоспособности для себя, когда я вижу что-то, что не имеет смысла, я всегда думаю, что сначала я делаю что-то не так в своем коде, затем я посмотрю на свои предположения о том, как что-то работает, и если они все еще кажутся здоровыми, я пойду искать других людей, у которых есть похожие проблемы, и посмотрю, есть ли у них решения. И, если проблема заключается в ядре языка, я покопаюсь в источнике или поговорю с некоторыми из разработчиков ядра и спрошу, не схожу ли я с тем, что вижу. Раньше я находил низкоуровневые ошибки, но они были угловыми случаями, и я потратил пару дней на копания, прежде чем что-то упоминать, потому что я не хотел быть таким, как мой коллега, который подал бы сообщение об ошибке немедленно с Apple, затем узнайте, что это ошибка в его коде.

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

И, по большому счету, выделение 1 000 000 элементов массива такого размера не занимает много памяти, учитывая, сколько у нас обычно свободно в наших коробках. Я был бы более обеспокоен необходимостью сохранять 1 000 000 элементов в массиве в памяти и рекомендовал бы коллегам, что они должны серьезно относиться к использованию базы данных. У вас может быть веская деловая причина для хранения всего этого в оперативной памяти. Если это так, вы можете увеличить объем оперативной памяти на хосте, и все будет в порядке.

1 голос
/ 11 мая 2011

Объекты должны быть собраны мусором. Если вы создадите их снова, процесс не должен значительно расти, потому что в нем есть все это пустое пространство. Тем не менее, Ruby не освобождает эту память обратно в ОС, поскольку предполагает, что в будущем ему, вероятно, понадобится столько памяти.

Это довольно упрощенное объяснение, но в целом то, что вы видите, нормально.

0 голосов
/ 26 января 2012

Что @digitalross можно более или менее увидеть, если вы запустите выделение дважды. Если бы действительно была такая утечка памяти, вы могли бы ожидать, что объем памяти удвоится, однако этого не происходит.

[~]$ irb                                         rvm:ruby-1.9.3-p0@global 
1.9.3p0 :001 > `ps -o rss= -p #{Process.pid}`.to_i
 => 8148 
1.9.3p0 :002 > a = {}
 => {} 
1.9.3p0 :003 > 1000000.times{|i| a[i] = "test #{i}"}
 => 1000000 
1.9.3p0 :004 > `ps -o rss= -p #{Process.pid}`.to_i
 => 101188 
1.9.3p0 :005 > 1000000.times{|i| a.delete(i)}
 => 1000000 
1.9.3p0 :006 > `ps -o rss= -p #{Process.pid}`.to_i
 => 90960 
1.9.3p0 :007 > GC.start
 => nil 
1.9.3p0 :008 > `ps -o rss= -p #{Process.pid}`.to_i
 => 93388 
1.9.3p0 :009 > `ps -o rss= -p #{Process.pid}`.to_i
 => 93388 
1.9.3p0 :010 > 1000000.times{|i| a[i] = "test #{i}"}
 => 1000000 
1.9.3p0 :011 > `ps -o rss= -p #{Process.pid}`.to_i
 => 140088 
1.9.3p0 :012 > 1000000.times{|i| a.delete(i)}
 => 1000000 
1.9.3p0 :013 > `ps -o rss= -p #{Process.pid}`.to_i
 => 130880 
1.9.3p0 :014 > GC.start
 => nil 
1.9.3p0 :015 > `ps -o rss= -p #{Process.pid}`.to_i
 => 104256 

В конце первого запуска процесс сообщает о размере памяти 93388, а после второго запуска сообщает 104256, только использование памяти увеличивается примерно на 10%.

...