La ultima con la que me he topado ha sido un OutOfMemoryException al parsear un archivo de texto...
Me he puesto a analizar el dump de la pila de la JVM con el plugging de eclipse Memory Analizer y que gran sorpresa al ver que había un montón de strings de varios megas de tamaño....
Así que me he dispuesto a mirar el código y por alguna extraña razón me ha olido mal String.substring() y me he puesto a ver si era el problema: Todas mis sospechas, TODAS mis sospechas se han hecho realidad. En resumida cuentas, la documentación oficial de oracle dice:
public String substring(int beginIndex,
int endIndex)
Returns a new string that is a substring of this string. The substring begins at the specified beginIndex and extends to the character at index endIndex - 1. Thus the length of the substring is endIndex-beginIndex.
Examples:
"hamburger".substring(4, 8) returns "urge"
"smiles".substring(1, 5) returns "mile"
Parameters:
beginIndex - the beginning index, inclusive.
endIndex - the ending index, exclusive.
Returns:
the specified substring.
Y 'MIENTE'. Parece ser que String tiene, aparte de la cadena de caracteres, unos índices de inicio y de fin de cadena que inicialmente están a 0 y String.length() respectivamente. El problema es que lo que hace substring NO es devolver un nuevo String sino una copia de el mismo string moviendo los dos indices: el 0 a beginindex y el final a finalindex. Me explico: el objeto en si es nuevo, pero apunta al mismo char[] interno, con lo cual si hacemos un substring de un caracter nos quedamos con TODO el contenido del string original en memoria...
Con lo cual cada vez que haces un String.substring tienes como resultado en memoria de la String original. Esto tal vez no sea mucho problema habitualmente pero si la cadena es larga y tienes que tratarla facilmente te supondrá un programa de memoria y de rendimiento importante.
Por ejemplo, imagina que buscaras una palabra de cada linea de un gran archivo de texto: meterias todas las lineas del archivo en memoria lo cual te puede dar un OutOfMemoryException.
Evitarlo es bastante sencillo, basta con sustituir:
String subs = str.substring(init,end);
Por:
String subs = new String(str.substring(init,end));
De este modo crea una nueva copia del String copiando el texto del substring en un nuevo char[].
Pero bueno, es algo a tener bastante en cuenta porque aparte de con substring también puedes tener problemas cuando la JVM utilice internamente substring lo cual ocurre, por ejemplo, en:
String.split(pattern)
Y al obtener grupos de regex.
Lo dicho: mucho ojo con los Strings.
No hay comentarios:
Publicar un comentario