Usé una variable con muchos datos, digamos String data
. Quería usar una pequeña parte de esta cadena de la siguiente manera:
this.smallpart = data.substring(12,18);
Después de algunas horas de depuración (con un visualizador de memoria) descubrí que el campo de objetos smallpart
recordaba todos los datos data
, aunque solo contenía la subcadena.
Cuando cambié el código a:
this.smallpart = data.substring(12,18)+"";
..¡El problema fue resuelto! ¡Ahora mi aplicación usa muy poca memoria ahora!
¿Cómo es eso posible? ¿Alguien puede explicar esto? Creo que this.smallpart siguió haciendo referencia a los datos, pero ¿por qué?
ACTUALIZACIÓN: ¿Cómo puedo borrar la cadena grande entonces? ¿Data = new String (data.substring (0,100)) hará lo mismo?
java
performance
string
memory
hsmit
fuente
fuente
new String(String)
; ver stackoverflow.com/a/390854/8946 .Respuestas:
Haciendo lo siguiente:
crea un nuevo objeto String (más pequeño) y descarta la referencia a la cadena creada por substring (), lo que permite la recolección de basura de este.
Lo importante a tener en cuenta es que
substring()
proporciona una ventana a una Cadena existente , o más bien, la matriz de caracteres subyacente a la Cadena original. Por lo tanto, consumirá la misma memoria que la cadena original. Esto puede ser ventajoso en algunas circunstancias, pero problemático si desea obtener una subcadena y deshacerse de la cadena original (como ha descubierto).Eche un vistazo al método substring () en la fuente JDK String para obtener más información.
EDITAR: Para responder a su pregunta complementaria, la construcción de una nueva Cadena a partir de la subcadena reducirá el consumo de memoria, siempre y cuando haya cualquier referencia a la Cadena original.
NOTA (enero de 2013). El comportamiento anterior ha cambiado en Java 7u6 . El patrón de peso mosca ya no se usa y
substring()
funcionará como es de esperar.fuente
String(String)
constructor (es decir, el constructor de cadenas que toma una cadena como entrada) es útil:new String(data.substring(x, y))
efectivamente hace lo mismo que anexar""
, pero aclara un poco la intención.value
atributo de la cadena original. Creo que por eso se mantiene la referencia.Si observa la fuente de
substring(int, int)
, verá que devuelve:donde
value
es el originalchar[]
. Entonces obtienes una nueva cadena pero con el mismo subyacentechar[]
.Cuando lo haces,
data.substring() + ""
obtienes una nueva cadena con un nuevo subyacentechar[]
.En realidad, su caso de uso es la única situación en la que debe usar el
String(String)
constructor:fuente
new String(String)
; ver stackoverflow.com/a/390854/8946 .Cuando lo usas
substring
, en realidad no crea una nueva cadena. Todavía se refiere a su cadena original, con una restricción de desplazamiento y tamaño.Por lo tanto, para permitir que se recopile su cadena original, debe crear una nueva cadena (usando
new String
, o lo que tiene).fuente
Como las cadenas de Java consisten en una matriz de caracteres, un desplazamiento inicial y una longitud (y un código hash en caché). Algunas operaciones de cadena como
substring()
crear un nuevo objeto de cadena que comparte la matriz de caracteres del original y simplemente tiene diferentes campos de desplazamiento y / o longitud. Esto funciona porque la matriz de caracteres de una cadena nunca se modifica una vez que se ha creado.Esto puede ahorrar memoria cuando muchas subcadenas se refieren a la misma cadena básica sin replicar partes superpuestas. Como ha notado, en algunas situaciones, puede evitar que se recopilen datos que ya no se necesitan.
La forma "correcta" de arreglar esto es el
new String(String)
constructor, es decirPor cierto, la mejor solución general sería evitar tener cadenas muy grandes en primer lugar y procesar cualquier entrada en fragmentos más pequeños, unos pocos KB a la vez.
fuente
new String(String)
; ver stackoverflow.com/a/390854/8946 .En Java, las cadenas son objetos inmutables y una vez que se crea una cadena, permanece en la memoria hasta que el recolector de basura la limpia (y esta limpieza no es algo que pueda dar por sentado).
Cuando llama al método de subcadena, Java no crea una cadena realmente nueva, sino que simplemente almacena un rango de caracteres dentro de la cadena original.
Entonces, cuando creaste una nueva cadena con este código:
en realidad creó una nueva cadena cuando concatenó el resultado con la cadena vacía. Es por eso.
fuente
Como documentado por jwz en 1997 :
fuente
En resumen, si crea muchas subcadenas a partir de una pequeña cantidad de cadenas grandes, use
Como solo usa el espacio para almacenar las cadenas grandes, pero si está extrayendo un puñado de cadenas pequeñas, de las pérdidas de cadenas grandes, entonces
Mantendrá su uso de memoria bajo, ya que las cadenas grandes se pueden recuperar cuando ya no se necesitan.
Que llame
new String
es un recordatorio útil de que realmente está obteniendo una nueva cadena, en lugar de una referencia a la original.fuente
new String(String)
; ver stackoverflow.com/a/390854/8946 .En primer lugar, la llamada
java.lang.String.substring
crea una nueva ventana en el originalString
con el uso del desplazamiento y la longitud en lugar de copiar la parte significativa de la matriz subyacente.Si echamos un vistazo más de cerca al
substring
método, notaremos una llamada al constructor de cadenasString(int, int, char[])
y la pasamos enterachar[]
que representa la cadena . Eso significa que la subcadena ocupará tanta cantidad de memoria como la cadena original .Ok, pero ¿por qué
+ ""
resulta en demanda de menos memoria que sin ella?Hacer un
+
encendidostrings
se implementa a través de unaStringBuilder.append
llamada al método. Mire la implementación de este método enAbstractStringBuilder
clase nos dirá que finalmente lo haráarraycopy
con la parte que realmente necesitamos (lasubstring
).¿Alguna otra solución?
fuente
Agregar "" a una cadena a veces ahorrará memoria.
Digamos que tengo una gran cadena que contiene un libro completo, un millón de caracteres.
Luego creo 20 cadenas que contienen los capítulos del libro como subcadenas.
Luego creo 1000 cadenas que contienen todos los párrafos.
Luego creo 10.000 cadenas que contienen todas las oraciones.
Luego creo 100,000 cadenas que contienen todas las palabras.
Todavía solo uso 1,000,000 de caracteres. Si agrega "" a cada capítulo, párrafo, oración y palabra, utilizará 5,000,000 de caracteres.
Por supuesto, es completamente diferente si solo extrae una sola palabra de todo el libro, y todo el libro podría ser basura recolectada, pero no es porque esa palabra tenga una referencia a ella.
Y nuevamente es diferente si tiene una cadena de un millón de caracteres y elimina pestañas y espacios en ambos extremos, haciendo 10 llamadas para crear una subcadena. La forma en que funciona o funciona Java evita copiar un millón de caracteres cada vez. Hay compromiso, y es bueno si sabes cuáles son los compromisos.
fuente