Cómo funciona la memoria en Java

Una de las principales ventajas de Java desde su primera versión fue que los desarrolladores no tenían que preocuparse por la gestión de memoria, ya que Java podía mantenerla limpia y liberarls automáticamente. Pero cualquier buen desarrollador debe conocer los conceptos básicos sobre cómo Java maneja la memoria para evitar fugas de memoria (memory leaks) y cuellos de botella (bottlenecks).

Para empezar, Java divide la memoria en dos segmentos bien diferenciados:

  • Heap: instancias, variables,...
  • Non-Heap/Perm: Código, metadatos, ...

Como primer paso para optimizar la memoria en Java, debemos centrarnos en el Heap, ya que es lo que podemos "controlar". El Heap se divide en dos generaciones según su vida útil:

  • Young Generation
  • Old Generation

Normalmente la generación joven está compuesta de variables locales y objetos temporales. Mientras que la generación vieja suele componerse de estructuras que son necesarias durante toda la ejecución como configuraciones.

La generación joven se divide en dos:

  • Eden: Aquí es donde se crean los objetos inicialmente
  • Survivor: Es como el limbo a través del que se pasa de la generación Young a la generación Old .

El Recolector de Basura (Garbage Collector)

El recolector de basura es el sistema que se asegura de que la memoria esté limpia. Realiza dos tipos de tareas periódicas:

  • Minor Collection: Que revisa de forma rápida la generación joven.
  • Major Collection: Que revisa de forma exhaustiva toda la memoria, fundamentalmente la generación vieja.

El recolector de basura se ejecuta al mismo tiempo que la ejecución normal del programa. Cada ejecución implica una pequeña pausa (generalmente milisegundos) en todos los subprocesos que se ejecutan en ese momento. Mientras la memoria de la aplicación se mantiene en buen estado, el recolector de basura limitará sus acciones a recolecciones menores (minor collection), para no interferir con el flujo de la aplicación.

Different memory strategies

Existen varias implementaciones del Recolector de Basura, siendo la más común el Serial Collector. Esta implementación, además de ser la más sencilla, utiliza sólo un procesador. Si se está usando una máquina más potente, con varios procesadores y una buena cantidad de memoria física, se puede activar el Parallel Collector , que utiliza varias CPUs a la vez. Esto mejora la forma en que funciona el recolector de basura. También puede paralelizar el flujo de ejecución normal de la aplicación.

De todas formas, para el buen funcionamiento y limpieza de la memoria, conviene tener pequeños objetos temporales de vida corta antes que objetos largos y duraderos. Los pequeños objetos temporales se quedarán en el Eden, por lo que se recolectarán mucho antes y de forma mucho más rápida.

Así mismo, tener objetos en memoria sin usar, aunque no molesten a la ejecución del programa, ralentizan la ejecución del Recolector de Basura, porque tendrá que procesarlos una y otra vez en todas sus pasadas.

En algún momento puede parecer tentador forzar una ejecución del Recolector de Basura llamando a System.gc(). Sin embargo, esto lo único que hará será forzar una major collection de forma asíncrona, rompiendo completamente toda la heurística del Recolector de Basura. Es tan desaconsejable que hasta hay una opción en la máquina virtual para desactivar estas llamadas: -XX:+DisableExplicitGC

References

Para ayudar a la tarea del Recolector de Basura, existen tres tipos de references when defining objects:

  • Weak: No impide que el GC borre la instancia asociada.
  • Soft: El GC respeta el enlace y elimina la instancia solo si se necesita memoria. Útil para el almacenamiento en caché, pero puede ser engañoso.
  • Phantom: Siempre devuelve nulo. El enlace realmente no apunta al objeto. Se puede usar para borrar instancias antes de tomar el objeto que lo une.

Por ejemplo, podemos usar WeakHashMap, que se usa como un HashMap, pero usando referencias Weak. De esta forma, si los objetos que contiene sólo están referenciados en dicho HashMap, considera que ya no son útiles y los borra.

FastJTable

Swing JTables of Java by default does not handle frequent updates and a huge number of columns and rows. I created a lighted version of JTable called FastJTable based on the code of the Java Christmas Tree.

This implementation is pretty faster when handling huge amounts of data. Consider taking a look at how the memory works if this is your use case.

/**
* Based on Sun's CTTable (Christmas Tree):
* https://java.sun.com/products/jfc/tsc/articles/ChristmasTree/
*
* @author marias
*/

import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;

import javax.swing.CellRendererPane;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.TableModel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Based on CTTable from Christmas Tree:
* https://java.sun.com/products/jfc/tsc/articles/ChristmasTree/
*
* @author marias marias@emergya.es
*/
public class FastJTable extends JTable {
private static final Log log = LogFactory.getLog(FastJTable.class);
private static final long serialVersionUID = -3218140266706898440L;

private JScrollPane scrollPane;

public FastJTable(TableModel model) {
super(model);
}

public void updateUI() {
super.updateUI();
setUI(new FastTableUI(this));
}

private static class FastTableUI extends BasicTableUI {

public FastTableUI(FastJTable table) {
super();
installUI(table);
}

@Override
public void installUI(JComponent c) {
// Overriden to install our own CellRendererPane
super.installUI(c);
c.remove(rendererPane);
rendererPane = new FastCellRendererPane();
c.add(rendererPane);
}
}

/**
* FastCellRendererPane overrides paintComponent to NOT clone the Graphics
* passed in and NOT validate the Component passed in. This will NOT work if
* the painting code of the Component clobbers the graphics (scales it,
* installs a Paint on it...) and will NOT work if the Component needs to be
* validated before being painted.
*/
private static class FastCellRendererPane extends CellRendererPane {
private static final long serialVersionUID = 4811773663334451913L;
private JViewport viewport;

public FastCellRendererPane() {
super();
}

// Can be ignored, we don't exist in the containment hierarchy.
public void repaint() {
}

@Override
public void paintComponent(Graphics g, Component c, Container p, int x,
int y, int w, int h, boolean shouldValidate) {
try {
if (c == null || !isVisible(new Rectangle(x, y, w, h))) {
log.trace("No lo pintamos (" + c + ")");
return;
}

// if (p != null) {
// Color oldColor = g.getColor();
// g.setColor(p.getBackground());
// g.fillRect(x, y, w, h);
// g.setColor(oldColor);
// }

if (c.getParent() != this) {
this.add(c);
}

c.setBounds(x, y, w, h);

// As we are only interested in using a JLabel as the renderer,
// which does nothing in validate we can override this to do
// nothing, if you need to support components that can do
// layout,
// this will need to be commented out, or conditionally
// validate.
if (!(c instanceof JLabel))
c.validate();

// JComponent jc = (c instanceof JComponent) ? (JComponent) c
// : null;
// jc.setDoubleBuffered(true);

// Don't create a new Graphics, reset the clip and translate
// the origin.
Rectangle clip = g.getClipBounds(c.getBounds());
g.clipRect(x, y, w, h);
g.translate(x, y);
c.paint(g);
g.translate(-x, -y);
g.setClip(clip.x, clip.y, clip.width, clip.height);
c.setBounds(-w, -h, 0, 0);
} catch (Throwable t) {
log.error("Error al pintar el componente de la tabla ", t);
}
}

/**
* We only paint the visible parts of the JTable.
* @param rectangle visible
* @return if it has to be painted on screen
*/
public boolean isVisible(Rectangle rectangle) {
if (viewport == null)
return true;

			Rectangle visRect = viewport.getViewRect();
			int xmin = ((Double) rectangle.getMinX()).intValue();
			int ymin = ((Double) rectangle.getMinY()).intValue();
			int xmax = ((Double) rectangle.getMaxX()).intValue();
			int ymax = ((Double) rectangle.getMaxY()).intValue();
			return (visRect.contains(new Point(xmin, ymin))
					|| visRect.contains(new Point(xmax, ymin))
					|| visRect.contains(new Point(xmin, ymax)) || visRect
					.contains(new Point(xmax, ymax)));
		}
	}
}

Decálogo de la Web Social

Si quieres que tu red social funcione, tienes que conocer al público objetivo. Aquí hay algunas guías cínicas para conseguir que tu Web Social funcione.

1.- La gente es egoísta

La gente tiende a querer hablar de sí mismos en todo momento y lugar. Nada les interesa más que su propia persona. Ofrecerles un perfil con muchos campos y un botón para subir esas fotos, y también esas otras fotos es algo indispensable. Si la red social no gira en torno a ellos, no les interesará.

No hace falta que te preocupes de la inclusividad o la diversidad, ellos crearán sus propios guettos.

2.- La gente es hipócrita

Las Redes Sociales son una manada de borrachos en fase eresmimejoramigo contínua. Una Web Social que se precie tiene que tener mecanismos (intercambio de mensajes) para pelotear al prójimo. Si pueden ser mecanismos públicos para que todo el mundo vea lo mucho que adoran a sus semejantes, mejor. Déjales mostrar lo increíblemente hipócritas que son.

3.- La gente está adolescentizada

La mayoría de la gente aún no pasó de esa fase adolescente de querer mimetizarse en un grupo. La Web Social debe permitir al usuario acoplarse a comunidades de usuarios, no importa de lo que traten o si realmente no sirven para nada.

4.- La gente es tonta

Las Web Sociales demasiado complejas no suelen tener éxito, mayormente porque la gente no sabe usarlas. Simplificar y reducir al máximo las opciones. Nunca subestimes la torpeza del usuario medio, ellos encontrarán nuevas formas de romper la interfaz. Puedes esconder algunas features detrás de interfaces complejas para premiar a tus usuarios más avispados.

5.- La gente es prepotente

La gente necesita sentirse superior, ya sea protagonizando la portada de una web, haciendo chistes estúpidos o trolleando. Una Web Social que no te permita tus cinco minutos de fama está condenada al fracaso. Da oportunidades a tus usuarios para que se sientan importantes.