Ruby, объединяющий два массива, приводит к тому, что процессу не хватает памяти

Объединение двух массивов хэшей, каждый из которых содержит 100 тыс. элементов, приводит к тому, что процессу, работающему на виртуальной машине объемом 2 ГБ, не хватает памяти. Я не понимаю, как/почему.

Допустим, у меня есть такой хэш, и я заполняю его 50 парами ключ/значение.

h = {}
1..50.times{ h[SecureRandom.hex] = SecureRandom.hex}

Я помещаю 100k hs в два массива следующим образом:

a = []
a1 = []
1..100_000.times{ a << h }
1..100_000.times{ a1 << h }

Когда я пытаюсь добавить a1 в a, IRB заканчивается память:

2.1.1 :008 > a << a1
NoMemoryError: failed to allocate memory

Действительно ли два массива слишком велики, чтобы их можно было объединить в памяти? Каков предпочтительный способ сделать это?

Я запускаю ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-linux], и на виртуальной машине нет других запущенных процессов.


person user1032752    schedule 13.08.2014    source источник
comment
100к предметов? больше похоже на 5 000 000 предметов каждый. 50 * 100 000 = 5 миллионов. присоединение к ним приведет к созданию массива из 10 миллионов элементов.   -  person Marc B    schedule 13.08.2014
comment
Я показываю это с использованием 1,72 ГБ реальной памяти и 4,87 ГБ виртуальной на Ruby 2.1.2 в OSX. Так что да, вполне возможно, что у него заканчивается оперативная память ..   -  person Doon    schedule 13.08.2014
comment
У меня работает нормально. Стоит отметить, что в этих списках содержится 100 000 ссылок на один и тот же хэш. a << a1 также добавляет ссылку на a1 в конец. Может быть, вы хотите a += a1.   -  person tadman    schedule 13.08.2014
comment
Это не должно. Тем более, что a << a1 на самом деле ничего не копирует, кроме ссылки на a1. Я подозреваю, что на самом деле происходит то, что IRB не хватает памяти при попытке отобразить результирующий массив.   -  person Ajedi32    schedule 13.08.2014
comment
Я мог бы что-то упустить, но - хеш заполняется случайными парами ключ/значение пятьдесят раз, а затем хэш добавляется к каждому массиву 100 000 раз, создавая два массива по 100 000 элементов каждый, где каждый элемент представляет собой хэш с 50 парами k/v. . Нет?   -  person user1032752    schedule 13.08.2014
comment
Ты прав. Однако каждый элемент — это не просто a хеш с 50 парами k/v, это один и тот же хэш. Поэтому, вероятно, используется намного меньше памяти, чем вы ожидаете.   -  person Ajedi32    schedule 13.08.2014
comment
@tadman a += a1 вызывает то же самое.   -  person user1032752    schedule 13.08.2014
comment
@ Ajedi32 и @tadman не будет ли ссылка на тот же хеш использовать меньше памяти, а не больше? Кроме того, изменение его таким образом ` 1..100_000.times{ a ‹‹ h.clone }` вызывает ту же проблему. Можете ли вы сказать мне, каково ваше использование памяти, когда он работает? Спасибо!   -  person user1032752    schedule 14.08.2014
comment
@user1032752 user1032752 Да, ссылки используют меньше памяти. Я сказал это в своем комментарии. Попробуйте добавить точку с запятой после окончательного утверждения, чтобы указать IRB не отображать результат.   -  person Ajedi32    schedule 14.08.2014
comment
@ Ajedi32 - я думаю, вы правы в том, что IRB не может отображать вывод. Запуск a += a1; nil завершается мгновенно без всплеска памяти. Вы хотите представить ответ на это?   -  person user1032752    schedule 14.08.2014
comment
@ user1032752 Подойдет.   -  person Ajedi32    schedule 14.08.2014


Ответы (1)


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

Пример:

require 'securerandom'
require 'objspace'

h = {}
1..50.times{ h[SecureRandom.hex] = SecureRandom.hex}
a = []
a1 = []
1..100_000.times{ a << h }
1..100_000.times{ a1 << h }
a << a1; nil # Semicolon and nil are for IRB, not needed with regular Ruby

puts "Total memory usage: #{ObjectSpace.memsize_of_all/1000.0} KB"

Я получаю результат Total memory usage: 7526.543 KB; нет, где близко к 2 ГБ.

person Ajedi32    schedule 13.08.2014
comment
Еще раз спасибо, я спросил об этом при устранении неполадок с этим вопросом - ссылка - думаешь, ты вообще можешь помочь? - person user1032752; 14.08.2014
comment
Возможно, рендеринг самого списка в виде строки вызывает сбой памяти. - person tadman; 14.08.2014
comment
@tadman Может быть. Честно говоря, хотя я не уверен. Все, что я могу сделать, это указать вам на ObjectSpace как потенциальный ресурс для отладки. - person Ajedi32; 14.08.2014
comment
Сам массив довольно компактен, это просто ссылки, но расширение его до строки с inspect может быть очень болезненным. - person tadman; 14.08.2014