En realidad, por el motivo del tema, me permití profundizar en String.substring(..). Y probablemente obtuve resultados inesperados que decidí compartir con ustedes, queridos habitantes de Javorashov. Preséntelo a su juicio, por así decirlo. Asi que aqui esta. Hay una declaración de que una cadena creada usando el método substring(..) usa la matriz de caracteres de la cadena original. Aquí, en particular, hay un extracto del artículo recientemente leído "Referencia de Java. Cadenas estáticas" de los respetados artículos :
Hay una nota sobre el método de subcadena: la cadena devuelta utiliza la misma matriz de bytes que la original.Y por supuesto, Conferencias Javarash. Aquí hay citas del 22 de diciembre:
Cuando creamos una subcadena usando el método de subcadena, se crea un nuevo objeto String. Pero en lugar de almacenar una referencia a una matriz con un nuevo conjunto de caracteres, este objeto almacena una referencia a la matriz de caracteres anterior y al mismo tiempo almacena dos variables con las que determina qué parte de la matriz de caracteres original le pertenece. ... Cuando se crea una subcadena, la matriz de caracteres no se copia a un nuevo objeto String. En cambio, ambos objetos almacenan una referencia a la misma matriz de caracteres. ¡Pero! El segundo objeto almacena dos variables más, que contienen cuáles y cuántos caracteres de esta matriz se escriben en él. ... Por lo tanto, si toma una cadena de 10.000 caracteres y crea 10.000 subcadenas de cualquier longitud a partir de ella, estas "subcadenas" ocuparán muy poca memoria, porque la matriz de caracteres no está duplicada. Las cadenas que deberían ocupar una gran cantidad de espacio solo ocuparán un par de bytes.Todo está claramente escrito, incluso masticado. Pero, como estoy tratando de mejorar mis conocimientos de inglés, a menudo recurro a la documentación oficial, y de alguna manera no pude encontrar la confirmación de este hecho... Atribuyendo esto a mi descuido, todavía miré el código fuente de substring() (gracias a IDEA te permite hacer esto con solo hacer clic en un botón).
public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); }
Intrigado, fui más allá: * Allocates a new {@code String} that contains characters from a subarray * of the character array argument. The {@code offset} argument is the * index of the first character of the subarray and the {@code count} * argument specifies the length of the subarray. The contents of the * subarray are copied; subsequent modification of the character array does * not affect the newly created string. public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); }
donde Arrays.copyOfRange es un método nativo que devuelve una copia de una matriz de char... Código bastante trivial, y me pareció obvio que simplemente se crea una nueva fila con un nuevo conjunto de caracteres. o no tomé algo en cuenta... Entonces, sin creer completamente en mis conclusiones, decidí probar de alguna manera esta subcadena(), basándome en una frase de la conferencia:
Por lo tanto, si tomas una cadena de 10.000 caracteres de largo y creas 10.000 subcadenas de cualquier longitud a partir de ella, entonces estas “subcadenas” ocuparán muy poca memoria...Solo que en lugar de 10_000 ganaremos inmediatamente 100_000_000, ¿por qué perder el tiempo en nimiedades? Rápidamente ingresé el siguiente código: y esto es lo que sucedió: es decir. Cada vez que crea una nueva subcadena usando bigString.substring(..), la matriz de caracteres se DUPLICA. ¿De qué otra manera podemos explicar tal aumento en el consumo de memoria? Después de esto, personalmente ya no tuve dudas sobre el funcionamiento del método String.substsring() ¿Y tú?
public class Test { public static void main(String[] args) { System.out.println("Начинаем:"); print(); System.out.println("********************************"); char[]big=new char[100_000_000];//создаем нормальный такой массив int j=0;//и заполняем этот массив всякой ерундой for (int k=0;k
list=new ArrayList<>();//здесь будут ссылки на строки, что бы сборщик мусора не удалял //не используемые, по его мнению, строки. System.out.println("************************************"); System.out.println("Теперь будем создавть подстроки с помощью substring(..) и наблюдать," + "что же происходит с памятью"); for (int i = 2; i <10; i++) { //создаем подстроку, используя метод String.substring(..) String sub= bigString.substring(1,bigString.length()-1); //если этот метод не создает completamente новый массив символов, а только пользуется //исходным из bigString // то при создании новой строки sub мы не будем наблюдать ощутипый расход памяти list.add(sub);//эти ссылки мы должны где нибудь хранить, иначе сборщик мусора //избавится от неипользуемых объктов String System.out.print(String.format("Создаем %d-ую подстроку, при этом ", i - 1)); print(); } System.out.println("***************************************"); print(); } static void print(){ System.out.println("Памяти используется "+(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024 + " mb"); } }
Начинаем: Памяти используется 0 mb ******************************** создал большую строку bigString на основе массива big. Теперь: Памяти используется 382 mb ************************************ Теперь будем создавть подстроки с помощью substring(..) и наблюдать,что же происходит с памятью Добавляем 1-ую подстроку, при этом Памяти используется 573 mb Добавляем 2-ую подстроку, при этом Памяти используется 763 mb Добавляем 3-ую подстроку, при этом Памяти используется 954 mb Добавляем 4-ую подстроку, при этом Памяти используется 1145 mb Добавляем 5-ую подстроку, при этом Памяти используется 1336 mb Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:3658) at java.lang.String.
(String.java:201) at java.lang.String.substring(String.java:1956) at com.javarush.test.tests.Test.main(Test.java:42) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) Process finished with exit code 1
GO TO FULL VERSION