2012-05-24 17 views
8

Tenemos una aplicación web java/jruby ejecutándose bajo Tomcat, y he estado analizando la cantidad de objetos y el uso de memoria de la aplicación durante el tiempo de ejecución. He notado que después del inicio la clase "org.jruby.RubyString" tenía 1,118,000 instancias de la cadena "", la cantidad total de memoria de montón usada por cadenas vacías solo es de 65mb, esto para mí es ridículo porque es 15% de la memoria utilizado por la aplicación web. La cadena vacía es solo un ejemplo de muchos valores de cadena con este problema, si puedo internar todas las cadenas de jruby que resolví, podría ahorrar unos 130mb.¿Hay alguna manera de hacer que el tiempo de ejecución de Jruby sea interno en todas las cadenas?

Sé en Java, cada vez que se crea un valor de cadena, comprobará si el valor ya existe en el grupo de cadenas y lo reutilizará si lo hace. Me pregunto si hay una opción en Jruby que tenga la misma optimización. si es así, ¿cómo lo habilito?

Ejemplo de jruby:

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

Ejemplo en Java:

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

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

Si reemplazas todas las cadenas con símbolos, obtendrás ese comportamiento, pero no conozco ninguna opción para convertirlo en cadenas automáticas. –

+0

No es una solución ideal porque muchas de estas cadenas se crean a partir de gemas y complementos de terceros. –

+0

¿Puedes publicar una de las piezas de código que produce estas cadenas vacías? – peter

Respuesta

2
v1 = v2 = v3 = "a" 

sólo creará un objeto en Ruby, no tres.

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

Antes de hacer algo tan drástico como internar todas las cuerdas, me gustaría comprobar con otros usuarios de Tomcat si esta es la mejor manera de hacer frente a este problema. No uso Tomcat ni JRuby, pero sospecho fuertemente que este no es el mejor enfoque.

Editar Si cada objeto que se creó a partir de una "a" era el mismo objeto, modificar uno de ellos modificaría todas las demás cadenas. Eso sería una pesadilla de efecto secundario.

+0

Mi ejemplo en mi comentario anterior fue un ejemplo perezoso. Pruebe, a = "a", b = "a" y los 2 objetos tendrán diferentes object_id. –

1

La única forma de internar una Cadena en JRuby es llamar al to_sym o intern (alias entre ellos), y así convertirlos en símbolos, lo cual, como usted mencionó, no ayuda mucho con las gemas de terceros. No hay, por lo que yo sé, otra forma.

Esto está en línea con el comportamiento de resonancia magnética:

[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

Incluso llamar '# to_sym' no ayuda ya que en ese punto el objeto de cadena ya se ha creado. Tiene que ser un símbolo para empezar. – Theo

5

entiendo la motivación detrás de esto, pero en realidad no hay tal interruptor "mágica" en JRuby ...

De un fondo de Java que se siente tratando de ahorrar en cadenas, pero no se puede esperar que las cadenas se comporten de la misma manera en JRuby que en Java. Primero que nada, son un objeto completamente diferente. Me atrevería a decir que un Ruby String es más un Java StringBuilder.

Sin duda es un desperdicio tener tantas instancias "" por ahí, pero si ese código como usted menciona es código de terceros, no hay mucho que pueda hacer al respecto, a menos que tenga ganas de parchear mucho. Intentaré identificar los lugares de origen de la mayoría de las instancias y las refactorizaré, pero recuerde que hay algunas partes "complicadas" para guardar cadenas, por ej. con Hash:

{ 'foo' => 'bar' } 

Se podría conjeturar que esto crea objetos 3, pero podría estar equivocado; en realidad crea dos de los 'foo'.Como String es mutable (a menos que frozen?) es dup s la cadena y freeze s cuando se utiliza como clave Hash (y hay una buena razón para eso).

Además, tenga en cuenta que debe refactorizar de manera "inteligente": perfile los bits que está cambiando si no reduce la velocidad tratando de obtener recursos económicos en los casos asignados.

+0

+1 para la oración 'StringBuilder'. –

+0

Respuesta informativa, pero básicamente se reduce a "jruby (y probablemente ruby) las cadenas son ineficientes, así que vive con ello". Si bien esta puede ser la última palabra, no es reconfortante. – Glenn

+0

@Glenn lo siento, pero dado que hay un código de terceros involucrado, no hay una mejor respuesta, supongo. algunas gemas ya lo reconocen y ahorran en cadenas al congelarlas y almacenarlas en constantes, p. https://github.com/puma/puma/blob/master/lib/puma/const.rb, mientras que otros, desafortunadamente, suponen que pueden modificar los parámetros del método 'String' que reciben. en algún momento jruby (pero incluso MRI) podría hacer una heurística de tratar de reutilizar algunas constantes 'String'" pero dudo que haya mucho que puedan hacer, en su mayoría nos queda a los programadores ... – kares

0

Este es ahora el comportamiento predeterminado en JRuby. A partir de la versión 9.1, todos los literales de cadena congelados (por ejemplo, 'hello'.freeze) devuelven la misma instancia, y lo mismo ocurre con las cadenas literales utilizadas como claves hash (por ejemplo, stuff['thing']) y algunos otros casos. Ver JRuby issue #3491.

Si desea congelar agresivamente todos los literales de cadena, puede ejecutar JRuby (9.1+) y Ruby (2.3+) con --enable-frozen-string-literal, pero prepárese para que las cosas se rompan ya que la mayoría de las gemas suponen que las cadenas son mutables.

Cuestiones relacionadas