2012-05-24 3 views
8

У нас есть java/jruby webapp под управлением tomcat, и я анализировал количество объектов и использование памяти в приложении во время выполнения. Я заметил, что после запуска класс «org.jruby.RubyString» имел 1,118,000 экземпляров строки «», общее количество памяти кучи, используемое только пустыми строками, составляет 65 МБ, это для меня нелепо, потому что это 15% памяти используемый webapp. Пустая строка - это всего лишь один пример многих строковых значений с этой проблемой, если я могу ставить все строки jruby, которые я разработал, я мог бы сэкономить около 130 мб.Есть ли способ заставить Jruby работать во всех строках?

Я знаю на Java, каждый раз, когда создается строковое значение, он проверяет, существует ли значение в пуле строк и повторно использует его, если это произойдет. Мне интересно, есть ли в Jruby вариант с той же оптимизацией? если да, как мне включить его?

Пример в JRuby:

v1 = "a" 
v2 = "a" 
puts v1.object_id # => 3352 
puts v2.object_id # => 3354 

Пример в Java:

String v1 = "a"; 
String v2 = "a"; 

System.out.println(v1.hashCode()); # => 97 
System.out.println(v2.hashCode()); # => 97 
+0

Если вы замените все строки символами, вы получите это поведение, но я не знаю, как это сделать, чтобы автоматизировать строки. –

+0

Не идеальное решение, потому что многие из этих строк созданы из сторонних драгоценных камней и плагинов. –

+0

Можете ли вы опубликовать один из фрагментов кода, который создает эти пустые строки? – peter

ответ

2
v1 = v2 = v3 = "a" 

будет создать только один объект в Ruby, не три.

v1 = v2 = v3 = "a" # => "a" 
v1.object_id # => 10530560 
v2.object_id # => 10530560 
v1 << "ll the same" # => "all the same" 
v2 # "all the same" 

Прежде чем делать что-то, как резкое, как интернирование всех строк, я бы проверить с другими пользователями TOMCAT, если это лучший способ справиться с этой проблемой. Я не использую Tomcat, или JRuby, но я сильно подозреваю, что это не лучший подход.

Редактировать Если каждый объект, который был построен из «a», был одним и тем же объектом, а модификация одного из них изменила бы все остальные строки. Это было бы побочным эффектом кошмара.

+0

Мой пример в моем комментарии ранее был ленивым примером. Попробуйте, a = "a", b = "a", и у 2 объектов будет другой object_id. –

1

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

Это согласуется с поведением МРТ:

[email protected]:~$ rvm ruby-1.9.3-p0 
[email protected]:~$ irb 
1.9.3p0 :001 > a = "Hello World" 
=> "Hello World" 
1.9.3p0 :002 > b = "Hello World" 
=> "Hello World" 
1.9.3p0 :003 > a.object_id 
=> 20126420 
1.9.3p0 :004 > b.object_id 
=> 19289920 
+0

Даже вызов '# to_sym' не помогает, поскольку в этот момент объект строки уже создан. Это должен быть символ для начала. – Theo

5

Я понимаю мотивацию этого, но на самом деле нет такого «волшебного» переключателя в JRuby ...

От фона Java он чувствует temp для сохранения на строках, но вы не можете ожидать, что строки будут вести себя одинаково в JRuby, как в Java. Прежде всего, это совершенно другой объект. Я бы сказал, что Ruby String - это скорее Java StringBuilder.

Это, безусловно, пустая трата, когда вокруг есть так много "" экземпляров, но если этот код, как вы упоминаете, является сторонним кодом, вы не можете с ним поделать - если вы не чувствуете, что обезьяна исправляет много. Я попытался бы определить места, из которых большинство экземпляров исходят, и реорганизовать их, но помните, что есть некоторые «сложные» части при сохранении строк, например. с Hash:

{ 'foo' => 'bar' } 

Вы бы догадаться, это создает 3 объекта, но вы ошибаетесь; он фактически создает два из 'foo'.Так как a String изменен (если frozen?), то s строка и freeze s при использовании в качестве ключа Hash (и для этого есть веская причина).

Также имейте в виду рефакторинг «разумно» - профиль битов, которые вы меняете, если вы не замедляете работу, пытаясь дешево распределить по выделенным экземплярам.

+0

+1 для предложения 'StringBuilder'. –

+0

Информативный ответ, но в основном сводится к «jruby (и, вероятно, рубину), строки неэффективны, так что живите с ним». Хотя это может быть последнее слово, это не утешительно. – Glenn

+0

@Glenn извините, но, поскольку есть сторонний код, на самом деле нет лучшего ответа, я думаю. некоторые драгоценные камни уже подтверждают это и сохраняют строки, замораживая и сохраняя их в константах, например. https://github.com/puma/puma/blob/master/lib/puma/const.rb, в то время как другие, к сожалению, предполагают, что могут изменять параметры метода String, которые они получают. в какой-то момент jruby (но даже MRI) может сделать эвристику, пытаясь повторно использовать некоторые константы 'String' ', но я сомневаюсь, что они могут многое сделать, в основном у нас остаются программисты ... – kares

0

Это поведение по умолчанию в JRuby. Из версии 9.1 все замороженные строковые литералы (например, 'hello'.freeze) возвращают один и тот же экземпляр, и то же самое относится к литеральным строкам, используемым в качестве хеш-ключей (например, stuff['thing']) и нескольким другим случаям. См. JRuby issue #3491.

Если вы хотите агрессивно заморозить все строковые литералы, вы можете запускать как JRuby (9.1+), так и Ruby (2.3+) с --enable-frozen-string-literal, но приготовьтесь к тому, что вещи сломаются, поскольку большинство камней предполагают, что строки изменяемы.

Смежные вопросы