2012-05-24 5 views
8

Wir haben eine java/jruby Webapp unter Tomcat laufen, und ich habe die Anzahl der Objekte und Speicherverbrauch von der App zur Laufzeit analysiert. Ich habe bemerkt, nach dem Start der Klasse "org.jruby.RubyString" hatte 1.118.000 Instanzen der Zeichenfolge "", die gesamte Menge an Heap-Speicher von leeren Strings allein verwendet wird 65mb, das ist mir lächerlich, weil es 15% des Speichers ist Wird von der Webanwendung verwendet. Die leere Zeichenfolge ist nur ein Beispiel für viele String-Werte mit diesem Problem, wenn ich alle jruby-Strings internieren kann, die ich ausgearbeitet habe, könnte ich etwa 130 MB sparen.Gibt es eine Möglichkeit, die JRuby-Laufzeit intern alle Zeichenfolgen intern zu machen?

Ich weiß in Java, jedes Mal, wenn ein String-Wert erstellt wird, wird es überprüfen, ob der Wert bereits im String-Pool vorhanden ist und es wiederverwenden, wenn es tut. Ich frage mich, ob es eine Option in Jruby gibt, die dieselbe Optimierung hat? Wenn ja, wie aktiviere ich es?

Beispiel in JRuby:

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

Beispiel in Java:

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

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

Wenn Sie alle Strings durch Symbole ersetzen, erhalten Sie dieses Verhalten, aber ich kenne keine Option, um es intern Strings automatisch zu machen. –

+0

Keine ideale Lösung, da viele dieser Strings aus 3rd Party Gems und Plugins stammen. –

+0

können Sie einen der Codeabschnitte veröffentlichen, der diese leeren Zeichenfolgen erzeugt? – peter

Antwort

2
v1 = v2 = v3 = "a" 

Wird nur erstellen ein Objekt in Ruby, nicht drei.

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

Bevor etwas so drastisch wie Internierung alle Fäden zu tun, würde ich mit anderen Kater Benutzer überprüfen, ob dies der beste Weg ist, um mit diesem Problem umzugehen. Ich verwende weder Tomcat noch JRuby, aber ich vermute stark, dass dies nicht der beste Ansatz ist.

Bearbeiten Wenn jedes Objekt, das aus einem "a" erstellt wurde, dasselbe Objekt war, dann würde das Ändern eines dieser Objekte alle anderen Zeichenfolgen ändern. Das wäre ein Nebeneffekt Albtraum.

+0

Mein Beispiel in meinem Kommentar war ein faules Beispiel. Versuchen Sie, a = "a", b = "a" und die 2 Objekte haben unterschiedliche object_id. –

1

Die einzige Möglichkeit, einen String in JRuby zu internieren, ist to_sym oder intern (sie alias einander), und damit sie Symbole - was, wie Sie erwähnt, nicht wirklich für Edelsteine ​​von Drittanbietern helfen. Soweit ich weiß, gibt es keinen anderen Weg.

Dies steht im Einklang mit dem MRT Verhalten:

[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

Selbst das Aufrufen von '# to_sym' hilft nicht, da das String-Objekt zu diesem Zeitpunkt bereits erstellt wurde. Es muss ein Symbol für den Anfang sein. – Theo

5

ich die Motivation dahinter zu verstehen, aber es ist wirklich nicht so „Magie“ Schalter in JRuby ...

Von einem Hintergrund Java es sich anfühlt temping, um Zeichenfolgen zu speichern, aber Sie können nicht erwarten, dass sich Zeichenfolgen in JRuby genauso verhalten wie in Java. Vor allem sind sie ein ganz anderes Objekt. Ich würde so weit gehen zu sagen, dass ein Rubin String eher ein Java StringBuilder ist.

Es ist sicherlich eine Verschwendung, so viele "" Instanzen herumliegen zu haben, aber wenn dieser Code, wie du es erwähnst, ein Drittanbietercode ist, kannst du nicht viel dagegen tun - es sei denn du fühlst dich wie Affe flicken. Ich würde versuchen, die Orte zu identifizieren, aus denen die meisten Instanzen kommen, und sie umgestalten - aber denken Sie daran, dass es einige "knifflige" Teile gibt, um Saiten zu speichern, z. mit Hash:

{ 'foo' => 'bar' } 

Sie würde vermuten, diese drei Objekte erstellt, aber Sie würden sich irren; es schafft tatsächlich zwei der 'foo'.Da ein String veränderbar ist (es sei denn frozen?) es dup s die Zeichenkette und freeze s wenn verwendet als Hash Schlüssel (und es gibt einen guten Grund dafür).

Denken Sie auch daran, "intelligent" umzuformen - profilieren Sie die Bits, die Sie ändern, wenn Sie die Dinge nicht verlangsamen, indem Sie versuchen, auf zugewiesene Instanzen billig zu werden.

+0

+1 für den 'StringBuilder'-Satz. –

+0

Informative Antwort, aber im Grunde läuft es darauf hinaus, dass "jruby (und wahrscheinlich Ruby) Strings ineffizient sind, also lebe damit." Während dies das letzte Wort ist, ist es nicht beruhigend. – Glenn

+0

@Glenn Tut mir leid, aber da es einen 3rd Party Code gibt, gibt es wirklich keine bessere Antwort, denke ich. Einige Edelsteine ​​bestätigen dies bereits und speichern Strings durch Einfrieren und Speichern in Konstanten, z. https://github.com/puma/puma/blob/master/lib/puma/const.rb, während andere leider annehmen, dass sie 'String'-Methodenparameter ändern können, die sie erhalten. Irgendwann könnte Jruby (aber auch MRT) eine Heuristik machen, die versucht, einige "String" -Konstanten wiederzuverwenden, aber ich bezweifle, dass es viel zu tun gibt, es bleibt uns bei den Programmierern ... – kares

0

Dies ist jetzt das Standardverhalten in JRuby. Ab Version 9.1 geben alle eingefrorenen Zeichenfolgenliterale (z. B. 'hello'.freeze) die gleiche Instanz zurück, und dasselbe gilt für Literalfolgen, die als Hash-Schlüssel (z. B. stuff['thing']) und einige andere Fälle verwendet werden. Siehe JRuby issue #3491.

Wenn Sie alle Zeichenkettenliterale aggressiv einfrieren möchten, können Sie sowohl JRuby (9.1+) als auch Ruby (2.3+) mit --enable-frozen-string-literal ausführen, aber bereiten Sie Dinge vor, da die meisten Edelsteine ​​annehmen, dass Zeichenketten änderbar sind.

Verwandte Themen