String constant pool, manejo de objetos String en memoria

Aquí estamos, este es el primer post en mi Blog! Los objetivos principales serán llevar un diario sobre las cosas relevantes y no tan relevantes que vaya encontrando en mi labor diaria, sobre todo orientado a la tecnología Java.  Actualmente me desempeño como Ingeniero de Desarrollo en Tech and Solve en la ciudad de Medellín trabajando para diferentes proyectos en el sector de Seguros. He querido iniciar con un tema muy importante que es el manejo que le da la máquina virtual a los objetos String en memoria.  Es de esperar que los programas que codifiquemos cuenten con una gran cantidad de este tipo de objetos en memoria, en mi caso trabajo actualmente dándole mantenimiento a una aplicación basada en Swing con interfaces realmente complejas que lógicamente hacen un uso intensivo de objetos String, como es de esperar el uso de memoria es intensivo, y el heap crece exponencialmente todo el tiempo. Lo primero que debemos comprender es que los Objetos String son inmutables.Podemos crear un nuevo objeto de la clase String de diferentes maneras, en mi caso siempre uso la de la línea 3 que se muestra a continuación

  1. String foo = new String();
  2. String foo = new String("Sheppard");
  3. String foo = "Sheppard";
Ahora vamos a crear un nuevo objeto llamado foo2 y vamos a asignarle la referencia al primer objeto creado foo.
  1. String foo2 = foo;
Hasta aquí el comportamiento es el esperado para cualquier otro objeto en Java al cual le hagamos la asignación por referencia a un objeto compatible.  Sin embargo al utilizar funciones como: concat(), replace(), substring(), toLowerCase(), trim() occurren varios fenómenos que no son evidentes a primera vista.  Observemos el código siguiente:
  1. String foo = "jugando ";
  2. String foo2 = foo + "con Strings";
  3. foo2.concat(foo);
  4. foo.concat(foo);
  5. foo2 += "Constant Pool";
  6. foo = foo.concat(foo2);
  7. System.out.println(foo + "/" + foo2);

Vamos a determinar la cantidad de objetos String que va a crear la máquina virtual, línea por línea.  Recordemos que los Objetos String en Java son inmutables.

– En la primera línea creamos el objeto foo con el literal “jugando  “; aquí tenemos nuestro primer referencia a un objeto String inmutable.

– En la segunda línea creamos de forma explicita un nuevo Objeto String referenciado por la variable foo2; el cual es fue instanciado en la primera línea y creamos el objeto String “con Strings”; en este caso tenemos un objeto String que perdemos y no podemos referenciar directamente que es el creado con “con Strings”.  El String referenciado por la variable foo2 quedaría con la cadena “jugando con Strings”. Cómo vemos hasta este momento tenemos 3 objetos String creados por la máquina virtual.

– En la tercera línea se crea el 4 objeto String y nuevamente “perdemos” la posibilidad de tener una referencia al objeto. foo2.contact(foo), con la cadena “con Stringsjugando”

– En la cuarta línea nuevamente se crea un objeto String que “perdemos”.  Esta vez con la cadena “jugando jugando”;

– Quinta línea. En esta creamos un nuevo objeto String el cual hace referencia a una cadena construida por la concatenación de dos String usando el operador “+=” entre la variable foo2 y el objeto String construido con “Constant Pool”.  Hasta el momento tenemos 7 objetos String en memoria, haciendo parte del String constant pool.

– En la sexta línea tenemos foo = foo.concat(foo2); ahora tenemos una nueva cadena “jugando jugando con Strings Constant Pool”.  Hasta este momento tenemos en memoria 8 objetos String que hacen parte del String Constant Pool

– Por último System.out.println(foo + “/” + foo2); crea nuestro último objeto en memoria con la cadena “jugando jugando con Strings Constant Pool / jugando con Strings Constant Pool.

En este punto podemos estar preguntándonos por que la maquina virtual tiene implementado este comportamiento