El Javadoc sobre String.intern()
no da muchos detalles. (En pocas palabras: devuelve una representación canónica de la cadena, lo que permite comparar las cadenas internas usando ==
)
- ¿Cuándo usaría esta función a favor
String.equals()
? - ¿Hay efectos secundarios no mencionados en el Javadoc, es decir, más o menos optimización por parte del compilador JIT?
- ¿Hay otros usos de
String.intern()
?
Respuestas:
cuando necesita velocidad ya que puede comparar cadenas por referencia (== es más rápido que igual)
La desventaja principal es que debes recordar asegurarte de que realmente haces () todas las cadenas que vas a comparar. Es fácil olvidar intern () todas las cadenas y luego puede obtener resultados confusamente incorrectos. Además, por el bien de todos, asegúrese de documentar muy claramente que confía en las cadenas que se están internalizando.
La segunda desventaja si decide internalizar cadenas es que el método intern () es relativamente costoso. Tiene que administrar el conjunto de cadenas únicas para que haga un poco de trabajo (incluso si la cadena ya se ha internalizado). Por lo tanto, tenga cuidado en el diseño de su código para que, por ejemplo, intern () todas las cadenas apropiadas en la entrada para que no tenga que preocuparse más por eso.
(de JGuru)
Tercera desventaja (solo Java 7 o menos): las cadenas internadas viven en el espacio PermGen, que generalmente es bastante pequeño; puede encontrarse con un OutOfMemoryError con mucho espacio de almacenamiento dinámico libre.
(de Michael Borgwardt)
fuente
if (s1.equals(s2))
yif (i1 == i2)
es mínima a menos que tenga muchas cadenas largas con los mismos personajes principales. En la mayoría de los usos del mundo real (que no sean URL), las cadenas diferirán entre los primeros caracteres. Y las largas cadenas if-else son un olor a código de todos modos: use enumeraciones y mapas de functor.Esto no tiene (casi) nada que ver con la comparación de cadenas. El internamiento de cadenas está destinado a ahorrar memoria si tiene muchas cadenas con el mismo contenido en su aplicación. Al usar
String.intern()
la aplicación, solo tendrá una instancia a largo plazo y un efecto secundario es que puede realizar una comparación de igualdad de referencia rápida en lugar de una comparación de cadena normal (pero esto generalmente no es aconsejable porque es realmente fácil de romper olvidando internarse solo una sola instancia)fuente
str.intern()
cuando lostr
esté"Hello"
.String.intern()
definitivamente es basura recolectada en las JVM modernas.Lo siguiente NUNCA se queda sin memoria, debido a la actividad de GC:
Vea más (de mí) sobre el mito de String.intern no GCed () .
fuente
OutOfMemoryException
- no, no el código anterior, en mi cerebro : enlace al artículo de Java, que apunta a este artículo, que apunta al artículo de Java, que ... :-)Recientemente escribí un artículo sobre la implementación de String.intern () en Java 6, 7 y 8: String.intern en Java 6, 7 y 8 - agrupación de cadenas .
Espero que contenga suficiente información sobre la situación actual con la agrupación de cadenas en Java.
En una palabra:
String.intern()
en Java 6, porque entra en PermGenString.intern()
en Java 7 y Java 8: usa 4-5 veces menos memoria que rodar su propio grupo de objetos-XX:StringTableSize
(el valor predeterminado es probablemente demasiado pequeño; establezca un número Prime)fuente
Comparar cadenas con == es mucho más rápido que con equals ()
5 Tiempo más rápido, pero dado que la comparación de cadenas generalmente representa solo un pequeño porcentaje del tiempo de ejecución total de una aplicación, la ganancia general es mucho menor que eso, y la ganancia final se diluirá a un pequeño porcentaje.
String.intern () separa la cadena de Heap y la coloca en PermGen
Las cadenas internalizadas se colocan en un área de almacenamiento diferente: Generación permanente, que es un área de la JVM que está reservada para objetos que no son de usuario, como Clases, Métodos y otros objetos JVM internos. El tamaño de esta área es limitado y es mucho más valioso que el montón. Al ser esta área más pequeña que Heap, hay más probabilidades de usar todo el espacio y obtener una OutOfMemoryException.
La cadena String.intern () es basura recolectada
En las nuevas versiones de JVM, las cadenas internalizadas son basura recolectada cuando ningún objeto hace referencia a ellas.
Teniendo en cuenta los 3 puntos anteriores, podría deducir que String intern () podría ser útil solo en pocas situaciones cuando realiza muchas comparaciones de cadenas, sin embargo, es mejor no usar cadenas internas si no sabe exactamente qué estás haciendo ...
fuente
Dado que hacen cosas diferentes, probablemente nunca.
Internar cadenas por razones de rendimiento para que pueda compararlas para la igualdad de referencia solo será beneficioso si mantiene referencias a las cadenas por un tiempo: las cadenas que provienen de la entrada del usuario o IO no serán internados.
Eso significa que en su aplicación usted recibe información de una fuente externa y la procesa en un objeto que tiene un valor semántico, como dice un identificador, pero ese objeto tiene un tipo indistinguible de los datos sin procesar y tiene diferentes reglas sobre cómo debería ser el programador úsalo.
Casi siempre es mejor crear un
UserId
tipo que está internado (es fácil crear un mecanismo de internamiento genérico seguro para subprocesos) y que actúa como una enumeración abierta, que sobrecargar eljava.lang.String
tipo con semántica de referencia si se trata de una ID de usuario.De esta forma, no se confunde si una Cadena en particular ha sido internada o no, y puede encapsular cualquier comportamiento adicional que necesite en la enumeración abierta.
fuente
No conozco ninguna ventaja, y si hubiera una, pensaría que equals () usaría internamente () internamente (lo que no hace).
Rompiendo mitos internos ()
fuente
intern
, y muy buenas razones para queequals
no lo haga de manera predeterminada. El enlace que publicaste es completo. El último párrafo incluso admite queintern
tiene un escenario de uso válido: procesamiento de texto pesado (por ejemplo, un analizador sintáctico). Concluir que "[XYZ] es peligroso si no sabes lo que estás haciendo" es tan banal que duele físicamente.Daniel Brückner tiene toda la razón. El internamiento de cadenas está destinado a ahorrar memoria (montón). Nuestro sistema actualmente tiene un hashmap gigante para almacenar ciertos datos. A medida que el sistema se escala, el hashmap será lo suficientemente grande como para hacer que el montón se quede sin memoria (como hemos probado). Al internar todas las cadenas duplicadas de todos los objetos en el mapa hash, nos ahorra una cantidad significativa de espacio de almacenamiento dinámico.
También en Java 7, las cadenas internadas ya no viven en PermGen, sino en el montón. Por lo tanto, no necesita preocuparse por su tamaño y sí, se recolecta la basura:
fuente
String
instancias usaban la mayor parte del espacio del montón . Al mirar su contenido, vi muchos duplicados y decidí cambiar aintern()
, lo que ahorró cientos de MB.No sé sobre el nivel JIT, pero existe un soporte directo de código de bytes para el conjunto de cadenas , que se implementa de manera mágica y eficiente con una
CONSTANT_String_info
estructura dedicada (a diferencia de la mayoría de los otros objetos que tienen representaciones más genéricas).JVMS
JVMS 7 5.1 dice :
Bytecode
También es instructivo observar la implementación de bytecode en OpenJDK 7.
Si descompilamos:
tenemos en el grupo constante:
y
main
:Tenga en cuenta cómo:
0
y3
:ldc #2
se carga la misma constante (los literales)12
: se crea una nueva instancia de cadena (con un#2
argumento)35
:a
yc
se comparan como objetos normales conif_acmpne
La representación de cadenas constantes es bastante mágica en el código de bytes:
new String
)y la cita JVMS anterior parece decir que siempre que el Utf8 apuntado es el mismo, se cargan instancias idénticas
ldc
.He realizado pruebas similares para campos y:
static final String s = "abc"
apunta a la tabla constante a través del atributo ConstantValueldc
Bonificación : compárelo con el grupo de enteros , que no tiene soporte directo de bytecode (es decir, no
CONSTANT_String_info
analógico).fuente
Examinaría intern y == - comparación en lugar de igual solo en el caso de que la comparación igual sea un cuello de botella en múltiples comparaciones de cadenas. Es muy poco probable que esto ayude con un pequeño número de comparaciones, porque intern () no es gratuito. Después de intercalar cadenas de forma agresiva, encontrará llamadas a intern () cada vez más lentas.
fuente
Un tipo de pérdida de memoria puede provenir del uso de
subString()
cuando el resultado es pequeño en comparación con la cadena de origen y el objeto tiene una larga vida.La solución normal es usar,
new String( s.subString(...))
pero cuando tiene una clase que almacena el resultado de un potencial / probablesubString(...)
y no tiene control sobre la persona que llama, puede considerar almacenar losintern()
argumentos de String pasados al constructor. Esto libera el gran búfer potencial.fuente
El internamiento de cadenas es útil en el caso de que el
equals()
método se invoque con frecuencia porque elequals()
método realiza una comprobación rápida para ver si los objetos son los mismos al comienzo del método.Esto generalmente ocurre cuando se busca a través de
Collection
otro código aunque también puede hacer verificaciones de igualdad de cadena.Sin embargo, el internamiento implica un costo, realicé un microbenchmark de algún código y descubrí que el proceso de internamiento aumenta el tiempo de ejecución en un factor de 10.
El mejor lugar para realizar la internación es generalmente cuando está leyendo claves almacenadas fuera del código, ya que las cadenas del código se internan automáticamente. Esto normalmente sucedería en las etapas de inicialización de su aplicación para evitar la penalización del primer usuario.
Otro lugar donde se puede hacer es al procesar la entrada del usuario que podría usarse para realizar búsquedas clave. Esto normalmente ocurre en su procesador de solicitudes, tenga en cuenta que las cadenas internados deben pasarse.
Aparte de eso, no tiene mucho sentido hacer prácticas en el resto del código, ya que generalmente no dará ningún beneficio.
fuente
Yo votaría por que no valga la pena el mantenimiento.
La mayoría de las veces, no habrá necesidad ni beneficio de rendimiento, a menos que su código trabaje mucho con las subcadenas. En cuyo caso, la clase String usará la cadena original más un desplazamiento para ahorrar memoria. Si su código usa muchas subcadenas, sospecho que solo hará que exploten sus requisitos de memoria.
fuente
http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html
afirma que
String.equals()
utiliza"=="
para compararString
objetos antes, de acuerdo conhttp://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html
compara las longitudes de cadenas y luego el contenido.
(Por cierto, las cadenas de códigos de productos en un catálogo de ventas pueden tener la misma longitud: BIC0417 es un casco de seguridad para ciclistas, TIG0003 es un tigre macho adulto vivo; probablemente necesite todo tipo de licencias para pedir uno de esos. Y tal vez sea mejor que pidas un casco de seguridad al mismo tiempo).
Por lo tanto, parece que obtiene un beneficio al reemplazar sus cadenas por su
intern()
versión, pero obtiene seguridad, legibilidad y cumplimiento estándar, sin usar "=="equals()
en su programación. Y la mayor parte de lo que voy a decir depende de que eso sea cierto, si es cierto.¿Pero
String.equals()
prueba que le pasó una cadena y no algún otro objeto antes de usar"=="
? No estoy calificado para decirlo, pero supongo que no, porque abrumadoramente la mayoría de estasequals()
operaciones serán de cadena a cadena, por lo que la prueba casi siempre se pasa. De hecho, priorizar "==" en el interiorString.equals()
implica la confianza de que con frecuencia está comparando la Cadena con el mismo objeto real.Espero que nadie se sorprenda de que las siguientes líneas produzcan un resultado de "falso":
Pero si cambias
i
ai.toString()
en la segunda línea, por supuesto que sítrue
.Los lugares donde podría esperar un beneficio de la pasantía incluyen
Set
yMap
, obviamente. Espero que las cadenas internas tengan sus códigos hash en caché ... Creo que eso sería un requisito. Y espero no haber regalado una idea que podría ganarme un millón de dólares. :-)En cuanto a la memoria, también es obvio que ese es un límite importante si su volumen de cadenas es grande, o si desea que la memoria utilizada por el código de su programa sea muy pequeña. Si su volumen de cadenas -distinct- es muy grande, entonces puede ser el momento de considerar el uso de un código de programa de base de datos dedicado para administrarlos y un servidor de base de datos separado. Del mismo modo, si puede mejorar un programa pequeño (que necesita ejecutarse en 10000 instancias simultáneamente) haciendo que no almacene sus cadenas en absoluto.
Se siente un desperdicio crear una nueva cadena y luego descartarla de inmediato para su
intern()
sustituto, pero no hay una alternativa clara, excepto para mantener la cadena duplicada. Entonces, realmente el costo de ejecución es buscar su cadena en el grupo interno y luego permitir que el recolector de basura elimine el original. Y si es un literal de cadena, entonces ya viene internado de todos modos.Me pregunto si el
intern()
código de programa malintencionado puede abusar de él para detectar si algunas cadenas y sus referencias a objetos ya existen en elintern()
grupo y, por lo tanto, existen en otra parte de la sesión de Java, cuando eso no se debe saber. Pero eso solo sería posible cuando el código del programa ya se esté utilizando de manera confiable, supongo. ¡Aún así, es algo a considerar sobre las bibliotecas de terceros que incluye en su programa para almacenar y recordar sus números PIN de cajero automático!fuente
La verdadera razón para usar pasante no es la anterior. Puede usarlo después de recibir un error de falta de memoria. Gran parte de la cadena en un programa típico es String.substring () de otra cadena grande [piense en extraer un nombre de usuario de un archivo xml de 100K. La implementación de Java es que, la subcadena contiene una referencia a la cadena original y el inicio + final en esa cadena enorme. (El pensamiento detrás de esto es una reutilización de la misma cuerda grande)
Después de 1000 archivos grandes, de los cuales solo guarda 1000 nombres cortos, ¡guardará en la memoria los 1000 archivos completos! Solución: en este escenario solo use smallsubstring.intern ()
fuente
Estoy usando el interno para ahorrar memoria, tengo una gran cantidad de datos de cadena en la memoria y al mover para usar el interno () guardé una gran cantidad de memoria. Desafortunadamente, aunque usa mucha menos memoria, la memoria que usa está almacenada en la memoria PermGen, no en Heap, y es difícil explicar a los clientes cómo aumentar la asignación de este tipo de memoria.
Entonces, ¿hay una alternativa a intern () para reducir el consumo de memoria (el == versus los beneficios de rendimiento iguales no es un problema para mí)
fuente
Seamos realistas: el escenario de caso de uso principal es cuando lee una secuencia de datos (ya sea a través de una secuencia de entrada o de un conjunto de resultados JDBC) y hay una gran cantidad de pequeñas cadenas que se repiten en todo momento.
Aquí hay un pequeño truco que le da cierto control sobre qué tipo de mecanismo le gustaría utilizar para internalizar cadenas y otros inmutables, y una implementación de ejemplo:
Lo uso a menudo cuando leo campos de secuencias o de ResultSets. Nota:
LRUCache
es un caché simple basado enLinkedHashMap<K,V>
. Llama automáticamente alretrieve()
método proporcionado por el usuario para todos los errores de caché.La forma de usar esto es crear uno
LRUInternalizer
antes de su lectura (o lecturas), usarlo para internalizar cadenas y otros pequeños objetos inmutables, luego liberarlo. Por ejemplo:fuente
Lo estoy usando para almacenar en caché el contenido de aproximadamente 36000 códigos que enlazan con nombres asociados. Interno las cadenas en el caché porque muchos de los códigos apuntan a la misma cadena.
Al internar las cadenas en mi caché, me aseguro de que los códigos que apuntan a la misma cadena realmente apunten a la misma memoria, lo que me ahorra espacio en la RAM.
Si las cadenas internas fueran realmente basura recolectada, no funcionaría para mí en absoluto. Esto básicamente negaría el propósito de la internación. El mío no será basura recolectada porque tengo una referencia a cada cadena en el caché.
fuente
El costo de internar una cadena es mucho más que el tiempo ahorrado en una sola cadenaA.equals (B) comparación. Solo úselo (por razones de rendimiento) cuando esté usando repetidamente las mismas variables de cadena sin cambios. Por ejemplo, si regularmente itera sobre una lista estable de cadenas para actualizar algunos mapas marcados en el mismo campo de cadena, puede obtener un buen ahorro.
Sugeriría usar internados de cadenas para ajustar el rendimiento cuando esté optimizando partes específicas de su código.
Recuerde también que las cadenas son inmutables y no cometen el error tonto de
recuerda hacer
fuente
Si está buscando un reemplazo ilimitado para String.intern, también basura recolectada, lo siguiente me está funcionando bien.
Por supuesto, si puede estimar aproximadamente cuántas cadenas diferentes habrá, simplemente use String.intern () con -XX: StringTableSize = highEnoughValue .
fuente