JavaRush /Blog Java /Random-ES /Pausa para el café #88. El poder de los metadatos: cómo t...

Pausa para el café #88. El poder de los metadatos: cómo trabajar con código espagueti. Recolección de basura en Java: cómo funciona y cuáles son sus ventajas

Publicado en el grupo Random-ES

El poder de los metadatos: cómo trabajar con código espagueti

Fuente: Hackernoon Todos intentamos utilizar enfoques comunes y patrones conocidos para crear una aplicación con el mínimo esfuerzo y el máximo impacto. Contamos con excelentes bibliotecas y marcos potentes que realizan operaciones de rutina por nosotros. Usamos todo esto para centrarnos únicamente en la lógica empresarial. Sin embargo, esta búsqueda a menudo nos lleva al código espagueti, especialmente cuando se trata de implementar una función sin una solución preparada para ello. En este artículo, quiero compartir con ustedes una herramienta poderosa que, en mi experiencia, no todos los desarrolladores aprecian. Esta herramienta está presente en la mayoría de los lenguajes de programación y se utiliza con mucha frecuencia en muchos marcos: anotaciones. Pausa para el café #88.  El poder de los metadatos: cómo trabajar con código espagueti.  Recolección de basura en Java: cómo funciona y cuáles son sus ventajas - 1

¿Te gustan los espaguetis?

Veamos un ejemplo que encontré hace un par de años. Necesitaba analizar una hoja de cálculo de Excel para colocar los datos analizados en una base de datos. También quería recopilar algunos de los datos de la base de datos y crear una hoja de cálculo. Para la implementación, utilicé la conocida biblioteca Java: Apache POI. La API de la biblioteca facilita su trabajo porque le permite crear manualmente una hoja, fila, celda y otros elementos. Esto es muy bueno, pero cuando es necesario generar varias hojas de cálculo de Excel, el código se vuelve completamente ilegible e insoportable. Como resultado, como suele ocurrir, la primera versión de la aplicación resulta sencillamente terrible. La implementación consistió en una clase de datos que representaba una cadena con todos los campos necesarios para el análisis. También había un analizador donde los campos de Excel se analizaban celda por celda y se colocaban en una instancia de clase de datos recién creada. Al principio el programa funcionó muy bien e hizo lo que se le pedía. Los problemas comenzaron cuando llegó el momento de hacer algunas modificaciones; el código no fue leído. Incluso yo, que escribí este código, no pude encontrar un lugar adecuado para colocar nuevas líneas para implementar la nueva función que necesitaba.

Rescate en anotaciones

Guardó la aplicación de este código de espagueti de anotación. Para deshacerme del código no compatible, necesitaba mover la lógica para determinar qué columna analizar, qué tipo de datos contenía una celda y todo lo demás a una ubicación diferente. Para hacer esto, creé una anotación en la que especificé el nombre de la columna para cada campo de clase. En la anotación, también agregué una variable que te permite seleccionar el color y la fuente de la celda. Por lo tanto, el código en la clase de análisis se ha reducido significativamente. Sólo un procesador creó dinámicamente la hoja de cálculo basándose en los parámetros tomados de las anotaciones. Fue una victoria. Luego, para realizar cambios en la aplicación, solo tuve que crear una clase con anotaciones. La solución recordaba a la biblioteca Jackson, que analiza JSON mediante anotaciones, y creo que no es necesario decir qué tan convenientes son Jackson o bibliotecas similares.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnExcel {

    String name() default "";

    int position();

    ExcelColumnDataFormat cellTypePattern() default ExcelColumnDataFormat.NONE;

    IndexedColors cellColor() default IndexedColors.AUTOMATIC;

    ExcelTotalFormula total() default ExcelTotalFormula.NONE;

}
ColumnExcel columnExcel = field.getAnnotation(ColumnExcel.class);
A medida que la aplicación evolucionó, recibió una nueva anotación que podía usarse para crear una celda en una hoja de cálculo con una función en su interior. Se pueden multiplicar, restar varios campos y se puede utilizar cualquier función común de Excel. También agregué una fila total para mostrar la suma por columna. E hice todo esto simplemente modificando ligeramente el analizador principal y simplemente agregando anotaciones a las clases.
@ColumnExcel(
            name = "Views",
            position = 4,
            total = ExcelTotalFormula.SUM)
    private BigDecimal variableC;

    @ColumnExcelFormula(
            name = "Conversion",
            position = 5,
            cellTypePattern = CellDataTypeFormatPattern.PERCENTAGE
    )
    public String variableD(int rowNumber) {
        return new CellAddress(rowNumber, 4).formatAsString() + "*"
		+ new CellAddress(rowNumber, 2).formatAsString();
    }

    @ColumnExcelTotalFormula(position = 4, cellTypePattern = CellDataTypeFormatPattern.RUR)
    public static String getVariableCTotalFormula(int firstRowNum, int lastRowNum) {
        return "SUM( " + new CellAddress(firstRowNum, 4).formatAsString() + ":"
		+ new CellAddress(lastRowNum, 4).formatAsString() + ")";
    }

Recolección de basura en Java: cómo funciona y cuáles son sus ventajas

Fuente: Dev.to La recolección de basura significa destruir o limpiar objetos no utilizados en la memoria. Java maneja la desasignación de memoria automáticamente porque una vez que se crea un objeto, usa algo de memoria en el montón. Pausa para el café #88.  El poder de los metadatos: cómo trabajar con código espagueti.  Recolección de basura en Java: cómo funciona y cuáles son sus ventajas - 2

¿Cómo funciona?

Antes de Java, el lenguaje de programación más popular era C o C++. Si hablas estos idiomas, debes saber que ellos administran su propia memoria manualmente. Por ejemplo, C tiene métodos como calloc() , malloc() y realloc() que le permitirán utilizar la memoria intermedia. Debe determinar cuánta memoria necesita para su programa y especificar cómo llama esta API. Luego puede obtener un búfer de memoria para crear un nodo de lista vinculada o algo más. Cuando su programa finaliza, en algún momento, usted también es responsable de limpiar esa memoria. Por lo tanto, una aplicación grande escrita en C sigue asignando memoria intermedia y, a veces, se olvida de vaciarla. En última instancia, esto provoca pérdidas de memoria y muchos problemas en la aplicación. A diferencia de C y C++, el lenguaje Java viene con administración automática de memoria a través de un hilo llamado recolector de basura. Su objetivo principal es liberar memoria del montón destruyendo objetos inaccesibles. El recolector de basura siempre se ejecuta en segundo plano.

¿Qué son los objetos inaccesibles en Java?

¿Cuándo tiene un objeto la oportunidad de iniciar la recolección de basura? Si hay objetos inaccesibles, aquellos para los que no hay enlaces activos. Veamos un ejemplo:
public static void main(String[] args)
{
// StringBuffer object sb is not eligible for garbage collection
StringBuffer sb = new StringBuffer("Flower Brackets");
System.out.println(sb);
// StringBuffer object sb is eligible for garbage collection
sb = null;
}
En el método principal creé un objeto StringBuffer y una referencia a él. En este punto, el objeto StringBuffer no es elegible para la recolección de basura. Ahora voy a configurar el objeto StringBuffer en "nulo". El objeto ahora es elegible para la recolección de basura y se convierte en un objeto inaccesible en la memoria del montón. Es decir, la recolección de basura generalmente funciona en los casos en que los objetos se vuelven inaccesibles. Esto significa que los objetos generalmente se crean en el contexto de un “bloque if” o método. Por lo tanto, los objetos salen del alcance una vez que se completa la ejecución del método y el recolector de basura puede eliminarlos. Debido a que las referencias de objetos antiguos a nuevos existen en cantidades limitadas, esto significa que los objetos que han estado presentes en su aplicación durante mucho tiempo generalmente no son objetos recién creados. Aquí hay un par de términos con los que deberíamos estar familiarizados; uno de ellos es un objeto vivo. Es un objeto en una aplicación al que hace referencia otro objeto en la misma aplicación. También hay un objeto "muerto". Un objeto muerto es un objeto inaccesible que se crea durante una llamada a un método y, una vez que se completa la llamada al método, el objeto sale de contexto y simplemente se queda en el montón.

¿Cuándo un objeto es elegible para la recolección de basura?

Si un objeto no tiene ninguna variable de referencia, entonces el objeto es elegible para la recolección de basura.

¿Cómo hacer que un objeto esté disponible para la recolección de basura?

A continuación se muestran varias formas:
  1. null reference variable
    Student obj = new Student();
    obj = null;

  2. re-assign reference variable
    Student obj1 = new Student();
    Student obj2 = new Student();
    obj1 = obj2;

  3. reate anonymous object
    new Student();

    Una vez que un objeto se pone a disposición del recolector de basura, no se destruye inmediatamente.

Cuando la máquina virtual Java ejecuta el recolector de basura, solo se destruye el objeto. NOTA: El recolector de basura solo recopila objetos creados con la palabra clave "nueva", para objetos sin la palabra clave "nueva", use el método finalize() . Existen varios métodos para ejecutar el recolector de basura en la máquina virtual Java:
  1. Método System.gc()

  2. método finalizar()

  3. Método Runtime.getRuntime().gc()

El método estático gc() se encuentra en la clase System . Este método le pide a la JVM que llame al recolector de basura. Veamos cómo una aplicación Java llama al recolector de basura usando el método gc() .
public class GarbageCollector
{
public static void main(String[] args)
{
Employee obj1 = new Employee();
Employee obj2 = new Employee();
obj1 = null;
obj2 = null;
System.gc();
}
public void finalize()
{
System.out.println("object garbage collected");
}
}
Resultado:
objeto recolectado como basura objeto recolectado como basura
El método finalize() se llama justo antes de que se limpie el objeto. Este método está definido en la clase Objeto :
protected void finalize() throws Throwable
  1. El método Finalize se utiliza para cerrar la conexión a la base de datos.

  2. Este método lo llama el recolector de basura, no la JVM.

  3. Necesitamos anular el método finalize() . Porque tiene una implementación vacía.

  4. Se llama sólo una vez por objeto.

El método getRuntime().gc() está presente en la clase de tiempo de ejecución. Devuelve el objeto Runtime asociado con la aplicación Java actual. Veamos este método en un programa Java.
public class Demo
{
public static void main(String[] args)
{
Demo obj1 = new Demo();
Demo obj2 = new Demo();
// nullifying reference variable
obj1 = null;
// nullifying reference variable
obj2 = null;
// running Garbage Collector
Runtime.getRuntime().gc();
}
@Override
protected void finalize() throws Throwable
{
System.out.println("Garbage collector called");
System.out.println("Object garbage collector: " + this);
}
}
Resultado:
Recolector de basura llamado Recolector de basura de objetos: Demo@2130772 Recolector de basura llamado Recolector de basura de objetos: Demo@cd4e940

Beneficios de la recolección de basura:

  1. La recolección de basura en Java se produce automáticamente, lo que nos ahorra la carga adicional de liberar memoria usada. Esto hace que la memoria del programa Java sea más eficiente.
  2. La recolección de basura garantiza la integridad del programa.
  3. No necesitamos escribir ningún código adicional ya que el recolector de basura es parte de la JVM.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION