2010-05-26 14 views
16

Si tiene dos instancias de String, y son iguales, en Java compartirán la misma memoria. ¿Cómo se implementa esto bajo el capó?¿Cómo implementa Java el patrón de peso mosca para la cuerda debajo del capó?

EDITAR: Mi aplicación utiliza una gran cantidad de objetos String, muchos de los cuales son idénticos. ¿Cuál es la mejor forma de utilizar el conjunto constante de Java String para evitar la creación de una implementación flyweight personalizada?

Respuesta

6

Mire el código fuente de java.lang.String (la fuente para toda la API de Java es parte del JDK).

En resumen: una cadena envuelve una subsecuencia de char[]. Ese respaldo char[] nunca se modifica. Esto se logra al no filtrar ni capturar este char[] fuera de la clase String. Sin embargo, varios Strings pueden compartir el mismo char[] (ver Implementación de String.substring).

También existe el mecanismo de internamiento, como se explica en las otras respuestas.

+0

El hecho de que 'String.substring' no asigne un nuevo' char [] 'ya no es verdadero. Ver [esta respuesta] (http://stackoverflow.com/a/14161077/4464702). – RAnders00

+0

Eso es correcto.String ya no implementa el patrón flyweight, porque el intercambio de referencia ahora se considera más costoso que reducir el "peso" de Strings, en parte porque las JVM se han mejorado para asignar objetos en la pila si el análisis de escape demuestra que el objeto no puede sobrevivir al marco de pila actual: una optimización que se basa en el objeto 'char []' que no se comparte. – meriton

4

Eso no es necesario cierto. Ejemplo:

String s1 = "hello"; 
String s2 = "hello"; 
System.out.println(s1 == s2); // true 

pero:

String s1 = new String("hello"); 
String s2 = new String("hello"); 
System.out.println(s1 == s2); // false 

Ahora la segunda forma se recomienda. Algunos (incluido yo) piensan que String ni siquiera debería tener un constructor público. Una versión mejorada de lo anterior sería:

String s1 = new String("hello").intern(); 
String s2 = new String("hello").intern(); 
System.out.println(s1 == s2); // true 

Es obvio que no es necesario hacer esto para una constante String. Es ilustrativo

Lo importante de esto es que si usted está aprobó una String o conseguir uno de una función que no se puede confiar en la String siendo canónica. Un canónicasObject satisface esta igualdad:

a.equals(b) == b.equals(a) == (a == b) 

para null casos no a, b, de un determinado Class.

+2

Una palabra de advertencia con respecto a la internación es que utiliza memoria PermGen, que puede dar lugar a un 'OutOfMemoryError' muy desagradable. Si es necesario agrupar cadenas, un grupo personalizado suele ser una mejor opción: http://hype-free.blogspot.com/2010/03/stringintern-there-are-better-ways.html – gustafc

+0

De Java 7 en adelante, cadenas internas ya no están en PermGen. Se [esta respuesta] (http://stackoverflow.com/a/16298053/4464702). @gustafc – RAnders00

6

Los literales de cadena están internados en Java, por lo que solo hay un objeto String con varias referencias (cuando son iguales, lo que no siempre es el caso). Vea el artículo de java.net All about intern() para más detalles.

También hay un buen ejemplo/explicación en la sección 3.10.5 String Literals del JLS que habla sobre cuándo se internan las cadenas y cuándo serán distintas.

12

Si tiene dos instancias de una cadena, y son iguales, en Java que compartirán la misma memoria

Esto en realidad no es 100% verdad.

This blog post is a decent explanation de por qué esto es así, y lo que String constant pool es.

+0

+1: Esta respuesta y la respuesta de Bill the Lizard son en realidad las que realmente abordan la cuestión. – haylem

3

Para responder a su pregunta editada, Sun JVM tiene una opción -XX:+StringCache, que en mi observación puede reducir la huella de memoria de una aplicación de String pesada de manera significativa.

De lo contrario, tiene la opción de internar sus cadenas, pero tendría cuidado al respecto. Las cadenas que son muy grandes y que ya no se referenciarán seguirán utilizando la memoria durante la vida útil de la JVM.

Editar (en respuesta al comentario): La primera vez que descubrí sobre la opción de StringCache here:

-XX: + StringCache Permite el almacenamiento en caché de cadenas comúnmente asignados.

Tom Hawtin describe algún tipo de almacenamiento en caché para mejorar algunos puntos de referencia. Mi observación cuando lo puse en IDEA fue que la huella de memoria (después de una recolección completa de basura) disminuyó por no tenerla. No es un parámetro documentado, y de hecho puede tratarse únicamente de la optimización de algunos puntos de referencia. Mi observación es que me ayudó, pero no construiría un sistema importante basado en eso.

+0

He intentado encontrar más información en -XX: + StringCache pero fue en vano. ¿Dónde puedo leer más sobre esta opción y cómo puede reducir la huella de memoria? ¿Tiene más información sobre qué le hace esta opción a VM? – Dan

1

Dos cosas que tener cuidado con:

  1. No utilice new String("abc") constructor, sólo tiene que utilizar el literal "abc".
  2. Aprenda a usar el método intern() en la clase String. Especialmente cuando se concatenan cadenas juntas o cuando se convierte una matriz/matriz de bytes/etc a una Cadena.

intern() devuelve siempre las cadenas que se agrupan.

0

Si sus Cadenas idénticas provienen de un conjunto fijo de valores posibles, entonces una Enumeración de Tipo Seguro es lo que desea aquí. No solo reducirá el número de hilos, sino que hará una aplicación más sólida. Toda su aplicación sabrá que esta cadena tiene semántica unida a ella, tal vez incluso algunos métodos de conveniencia.

Mis optimizaciones favoritas son siempre las que se pueden defender haciendo que el código sea mejor, no solo más rápido. Y 9 de cada 10 veces, reemplazar una cadena con un tipo concreto conduce a un código más correcto y autodocumentado.

Cuestiones relacionadas