¿Por qué agregar "" a una cadena ahorra memoria?


Usé una variable con muchos datos en ella, 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 objetos smallpart recordaba todos los datos de data, aunque solo contenía la subcadena.

Cuando cambié el código en:

this.smallpart = data.substring(12,18)+""; 

..el problema fue resuelto! Ahora mi aplicación utiliza muy poca memoria ahora!

¿Cómo es eso posible? Puede alguien explique esto? Creo que esto.smallpart siguió haciendo referencia a los datos, pero ¿por qué?

ACTUALIZACIÓN: ¿Cómo puedo limpiar la cuerda grande entonces? Will data = new String (data.substring(0,100)) hacer la cosa?

Author: hsmit, 2010-01-27

9 answers

Haciendo lo siguiente:

data.substring(x, y) + ""

Crea un nuevo objeto String (más pequeño), y tira la referencia a la String creada por substring(), permitiendo así la recolección de basura de esto.

Lo importante a tener en cuenta es que substring() da una ventana a una cadena existente, o mejor dicho, 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 quieres obtener una subcadena y disponer de la cadena original (como has averiguado).

Echa un vistazo al método substring () en la fuente de cadena JDK para más información.

EDITAR: Para responder a su pregunta suplementaria, construir una nueva cadena a partir de la subcadena reducirá su consumo de memoria, siempre que elimine cualquier referencia a la cadena original.

NOTA (enero de 2013). El comportamiento anterior ha cambiado en Java 7u6. El patrón de peso mosca es ya no se usa y substring() funcionará como cabría esperar.

 158
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-06 14:23:02

Si miras la fuente de substring(int, int), verás que devuelve:

new String(offset + beginIndex, endIndex - beginIndex, value);

Donde value es el original char[]. Así que obtienes una nueva cadena pero con el mismo subyacente char[].

Cuando lo haces, data.substring() + "", obtienes una nueva cadena con new subyacente char[].

En realidad, su caso de uso es la única situación en la que debe usar el constructor String(String):

String tiny = new String(huge.substring(12,18));
 27
Author: Pascal Thivent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-27 15:03:54

Cuando se usa 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.

Entonces, para permitir que tu cadena original sea recolectada, necesitas crear una nueva cadena (usando new String, o lo que tengas).

 17
Author: Chris Jester-Young,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-27 14:55:47

Creo que esto.parte pequeña guardada referencia hacia datos, pero ¿por qué?

Porque las cadenas Java consisten en una matriz char, un desplazamiento de inicio y una longitud (y un hashCode almacenado en caché). Algunas operaciones de cadena como substring() crean un nuevo objeto de cadena que comparte la matriz char del original y simplemente tiene diferentes campos de desplazamiento y/o longitud. Esto funciona porque la matriz char de una cadena nunca se modifica una vez que se ha creado.

Esto puede ahorrar memoria cuando se refieren muchas subcadenas a la misma cadena básica sin replicar partes superpuestas. Como ha notado, en algunas situaciones, puede evitar que los datos que ya no se necesitan se recopilen como basura.

La forma "correcta" de arreglar esto es el constructor new String(String), es decir,

this.smallpart = new String(data.substring(12,18));

Por cierto, la mejor solución general sería evitar tener cadenas muy grandes en primer lugar, y procesar cualquier entrada en trozos más pequeños, aa pocos KB a la vez.

 5
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-27 15:05:35

En Java las cadenas son objetos imutables y una vez que se crea una cadena, permanece en la memoria hasta que es limpiada por el recolector de basura (y esta limpieza no es algo que se puede dar por sentado).

Cuando llama al método substring, Java no crea una cadena completamente nueva, sino que solo almacena un rango de caracteres dentro de la cadena original.

Entonces, cuando creaste una nueva cadena con este código:

this.smallpart = data.substring(12, 18) + ""; 

En realidad ha creado una nueva cadena cuando concatena el resultado con la cadena vacía. Por eso.

 5
Author: Kico Lobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-26 09:23:18

Según lo documentado por jwz en 1997:

Si tienes una cadena enorme, saca una subcadena() de ella, agárrate a la subcadena y deja que la cadena más larga se convierta en basura (en otras palabras, la subcadena tiene una vida útil más larga) los bytes subyacentes de la cadena enorme nunca desaparecen.

 3
Author: Ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-27 15:13:43

Solo para resumir, si crea muchas subcadenas a partir de un pequeño número de cadenas grandes, use

   String subtring = string.substring(5,23)

Dado que solo usa el espacio para almacenar las cuerdas grandes, pero si está extrayendo un puñado de cuerdas pequeñas, de pérdidas de cuerdas grandes, entonces

   String substring = new String(string.substring(5,23));

Mantendrá su uso de memoria bajo, ya que las cadenas grandes pueden ser reclamadas cuando ya no sean necesarias.

Que llame new String es un recordatorio útil de que realmente está obteniendo una nueva cadena, en lugar de una referencia a el original.

 2
Author: mdma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-29 01:32:31

En primer lugar, llamando a java.lang.String.substring crea una nueva ventana en el original String 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 método substring notaremos que un constructor de cadenas llama a String(int, int, char[]) y lo pasa entero char[] que representa la cadena . Eso significa que la subcadena ocupará tanta cantidad de memoria como la original string .

Ok, pero ¿por qué + "" resulta en la demanda de menos memoria que sin ella??

Hacer un + en strings se implementa a través de la llamada al método StringBuilder.append. Mira la implementación de este método en la clase AbstractStringBuilder nos dirá que finalmente lo hace arraycopy con la parte que realmente necesitamos (la substring).

¿Alguna otra solución??

this.smallpart = new String(data.substring(12,18));
this.smallpart = data.substring(12,18).intern();
 2
Author: laika,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-02 14:34:39

Agregar "" a una cadena a veces ahorra memoria.

Digamos que tengo una cadena enorme que contiene un libro entero, un millón de caracteres.

Entonces creo 20 cadenas que contienen los capítulos del libro como subcadenas.

Entonces creo 1000 cadenas que contienen todos los párrafos.

Entonces creo 10.000 cadenas que contienen todas las oraciones.

Entonces creo 100.000 cadenas que contienen todas las palabras.

Todavía solo uso 1,000,000 caracter. Si agregas "" a cada capítulo, párrafo, frase y palabra, utilizas 5.000.000 de caracteres.

Por supuesto, es completamente diferente si solo extraes una sola palabra de todo el libro, y todo el libro podría ser basura recolectada, pero no lo es porque esa palabra contiene una referencia a ella.

Y es otra vez diferente si tiene una cadena de un millón de caracteres y elimina pestañas y espacios en ambos extremos, haciendo digamos 10 llamadas para crear una subcadena. La forma en que funciona Java o worked evita copiar un millón de caracteres cada vez. Hay compromiso, y es bueno si sabes cuáles son los compromisos.

 0
Author: gnasher729,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-27 19:43:39