Categorías
Memory

Recolección de basura Java Force – Ejemplo de código

No se puede aplicar la recolección de basura en Java. Pero aún así, a veces, llamamos al método System.gc () explícitamente. El método System.gc () proporciona solo una «pista» a la JVM de que se debe ejecutar la recolección de basura. ¡No está garantizado!

La documentación de la API para System.gc () establece que «Cuando el control regresa de la llamada al método, la máquina virtual Java ha hecho un gran esfuerzo para recuperar espacio de todos los objetos descartados». La responsabilidad del programador es asegurarse de que no queden referencias para los objetos.

El programa siguiente muestra que la recolección de basura puede ejecutarse después de que se llame a System.gc (). Nuevamente, NO está garantizado.

public class GCTest {
	public static void main(String[] args) throws InterruptedException {
		A a = new A("white");
		a = null;
 
		Runtime.getRuntime().gc();
	}
}
 
class A {
	private String color;
 
	public A(String color) {
		this.color = color;
	}
 
	@Override
	public void finalize() {
		System.out.println(this.color + " cleaned");
	}
}

Por lo tanto, el programa puede generar o no lo siguiente:

white cleaned

El siguiente programa muestra que la recolección de basura funciona automáticamente detrás sin System.gc () llamado.

public class GCTest {
	public static void main(String[] args) throws InterruptedException {
		A a = new A("white");
 
		for (int i = 0; i < 10000000; i++) {
			if (i % 2 == 1) {
				a = new A("red");
			} else {
				a = new A("green");
			}
			a = null;
		}
	}
}
 
class A {
	private String color;
 
	public A(String color) {
		this.color = color;
	}
 
	@Override
	public void finalize() {
		System.out.println(this.color + " cleaned");
	}
}

Producirá algo como lo siguiente:

...
green cleaned
green cleaned
red cleaned
red cleaned
green cleaned
green cleaned
red cleaned
red cleaned
green cleaned
green cleaned
red cleaned
red cleaned
...

Categorías
Diagram Memory

¿Cómo maneja Java el aliasing?

¿Qué es el alias de Java?

La creación de alias significa que hay varios alias en una ubicación que se pueden actualizar, y estos alias tienen diferentes tipos.

En el siguiente ejemplo, a y b son dos nombres de variable que tienen dos tipos diferentes A y B. B se extiende A.

B[] b = new B[10];
A[] a = b;
 
a[0] =  new A();
b[0].methodParent();

En la memoria, ambos se refieren a la misma ubicación.

La ubicación de la memoria señalada está señalada por ay b. Durante el tiempo de ejecución, el objeto real almacenado determina a qué método llamar.

¿Cómo maneja Java el problema de los alias?

Si copia este código en su eclipse, no habrá errores de compilación.

class A {
	public void methodParent() {
		System.out.println("method in Parent");
	}
}
 
class B extends A {
	public void methodParent() {
		System.out.println("override method in Child");
	}
 
	public void methodChild() {
		System.out.println("method in Child");
	}
}
 
public class Main {
 
	public static void main(String[] args) {
 
		B[] b = new B[10];
		A[] a = b;
 
		a[0] =  new A();
		b[0].methodParent();
	}
}

Pero si ejecuta el código, el resultado sería:

Exception in thread "main" java.lang.ArrayStoreException: aliasingtest.A
	at aliasingtest.Main.main(Main.java:26)

La razón es que Java maneja la creación de alias durante el tiempo de ejecución. Durante el tiempo de ejecución, sabe que el primer elemento debe ser un objeto B, en lugar de A.

Por lo tanto, solo se ejecuta correctamente si se cambia a:

B[] b = new B[10];
A[] a = b;
 
a[0] =  new B();
b[0].methodParent();

y la salida es:

override method in Child

Categorías
Diagram Memory

¿Cómo se ve una matriz de Java en la memoria?

En Java, una matriz almacena valores primitivos (int, char, …) o referencias (también conocidas como punteros) a objetos.

Cuando se crea un objeto utilizando «nuevo», se asigna un espacio de memoria en el montón y se devuelve una referencia. Esto también es cierto para las matrices, ya que las matrices son objetos en Java.

1. Matriz de una dimensión

int arr[] = new int[3];

El INT[] arr es solo la referencia a la matriz de 3 enteros. Si crea una matriz con 10 enteros, es lo mismo: se asigna una matriz y se devuelve una referencia.

2. Matriz bidimensional

¿Qué tal una matriz bidimensional? En realidad, solo podemos tener matrices unidimensionales en Java. Una matriz de 2 dimensiones es solo una matriz de matrices de 1 dimensión.

int[ ][ ] arr = new int[3][ ];
arr[0] = new int[3];
arr[1] = new int[5];
arr[2] = new int[4];

Matriz en memoria Java

Las matrices multidimensionales son similares a las que puedes imaginar.

3. ¿Dónde se encuentran en la memoria?

Las matrices también son objetos en Java, por lo que la apariencia de un objeto en la memoria se aplica a una matriz.

Como sabemos, las áreas de datos en tiempo de ejecución de JVM incluyen montón, pila de JVM y otras. Para un ejemplo simple como sigue, veamos dónde se almacenan la matriz y su referencia.

class A {
	int x;
	int y;
}
 
...
 
public void m1() {
	int i = 0;
	m2();
}
 
public void m2() {
	A a = new A();
}
 
...

Con la declaración anterior, invoquemos m1 () y veamos qué sucede:

  1. Cuando se invoca m1, se inserta un nuevo marco (Marco-1) en la pila y la variable local i también se crea en el Marco-1.
  2. Luego se invoca m2 dentro de m1, se inserta otro marco nuevo (Marco-2) en la pila. En m2, se crea un objeto de clase A en el montón y la variable de referencia se coloca en el Cuadro-2. Ahora, en este punto, la pila y el montón tienen el siguiente aspecto:

Java-matriz-en-memoria

Las matrices se tratan de la misma manera que los objetos, por lo que la ubicación de las matrices en la memoria es sencilla.

Categorías
Diagram JVM/Compiler Memory

Áreas de datos en tiempo de ejecución de JVM

Las siguientes son mis notas sobre la lectura de las especificaciones de JVM.

1. Áreas de datos para cada hilo individual (no compartido)

Las áreas de datos para cada subproceso individual incluyen registro de contador de programa, pila de JVM y pila de método nativo. Todos se crean cuando se crea un nuevo hilo.

Program Counter Register se utiliza para controlar cada ejecución de cada hilo.
JVM Stack contiene marcos que se muestran en el diagrama a continuación.
Native Method Stack se utiliza para admitir métodos nativos, es decir, métodos que no son de lenguaje Java.

2. Áreas de datos compartidas por todos los subprocesos

Todos los subprocesos comparten el área de método y montón.

Montón es el área con la que nos ocupamos con más frecuencia. Almacena matrices y objetos, creados cuando se inicia JVM. Garbage Collection trabaja en esta área.

El área de métodos almacena el grupo de constantes en tiempo de ejecución, los datos de campos y métodos, y el código de los métodos y constructores.

Runtime Constant Pool es una representación en tiempo de ejecución por clase o por interfaz de la tabla constant_pool en un archivo de clase. Contiene varios tipos de constantes, que van desde literales numéricos conocidos en tiempo de compilación hasta referencias a métodos y campos que deben resolverse en tiempo de ejecución.

Pila de JVM

La pila contiene marcos y se envía un marco a la pila cuando se invoca un método. Un marco contiene una matriz de variables locales, Operand Stack, Reference to Constant Pool.

Para obtener más información, visite el sitio oficial de especificaciones de JVM.

Referencias:
1. Especificación de JVM: áreas de datos en tiempo de ejecución
2. Fundamentos de código de bytes de Java

Categorías
Memory

Tutorial de reflexión de Java

¿Qué es la reflexión?

«Reflection es comúnmente utilizado por programas que requieren la capacidad de examinar o modificar el comportamiento en tiempo de ejecución de las aplicaciones que se ejecutan en la máquina virtual Java». Este concepto a menudo se mezcla con la introspección. Las siguientes son sus definiciones de Wiki:

  1. La introspección es la capacidad de un programa para examinar el tipo o las propiedades de un objeto en tiempo de ejecución.
  2. La reflexión es la capacidad de un programa para examinar y modificar la estructura y el comportamiento de un objeto en tiempo de ejecución.

Según sus definiciones, la introspección es un subconjunto de la reflexión. Algunos lenguajes admiten la introspección, pero no la reflexión, por ejemplo, C ++.

Ejemplo de introspección: el operador instanceof determina si un objeto pertenece a una clase en particular.

if(obj instanceof Dog){
   Dog d = (Dog)obj;
   d.bark();
}

Ejemplo de reflexión: el método Class.forName () devuelve el objeto Class asociado con la clase / interfaz con el nombre dado (una cadena y un nombre calificado completo). El método forName hace que se inicialice la clase con el nombre.

// with reflection
Class<?> c = Class.forName("classpath.and.classname");
Object dog = c.newInstance();
Method m = c.getDeclaredMethod("bark", new Class<?>[0]);
m.invoke(dog);

En Java, la reflexión se trata más de introspección, porque no se puede cambiar la estructura de un objeto. Hay algunas API para cambiar la accesibilidad de métodos y campos, pero no estructuras.

¿Por qué necesitamos la reflexión?

La reflexión nos permite:

  • Examinar la clase de un objeto en tiempo de ejecución
  • Construye un objeto para una clase en tiempo de ejecución
  • Examinar el campo y el método de una clase en tiempo de ejecución
  • Invocar cualquier método de un objeto en tiempo de ejecución
  • Cambiar el indicador de accesibilidad de Constructor, Método y Campo
  • etc.

La reflexión es la técnica común utilizada en los marcos.

Por ejemplo, JUnit usa la reflexión para mirar a través de métodos etiquetados con el @Prueba anotación, y luego llame a esos métodos cuando ejecute la prueba unitaria. (Aquí hay un conjunto de ejemplos de cómo usar JUnit).

Para los marcos web, los desarrolladores definen su propia implementación de interfaces y clases en los archivos de configuración. Mediante la reflexión, el marco inicializa dinámicamente las clases necesarias. Por ejemplo, Spring usa una configuración de frijoles como:

<bean id="someID" class="com.programcreek.Foo">
    <property name="someField" value="someValue" />
</bean>

Cuando el contexto de Spring procesa este elemento , usará Class.forName (String) con el argumento «com.programcreek.Foo» para instanciar esa clase. Luego volverá a usar la reflexión para obtener el setter apropiado para el elemento y establecerá su valor en el valor especificado.

El mismo mecanismo también se utiliza para las aplicaciones web de Servlet:

<servlet>
    <servlet-name>someServlet</servlet-name>
    <servlet-class>com.programcreek.WhyReflectionServlet</servlet-class>
<servlet>

¿Cómo utilizar la reflexión?

Se puede mostrar cómo usar la API de reflexión mediante un pequeño conjunto de ejemplos de código típicos.

Ejemplo 1: obtener el nombre de la clase del objeto

package myreflection;
import java.lang.reflect.Method;
 
public class ReflectionHelloWorld {
	public static void main(String[] args){
		Foo f = new Foo();
		System.out.println(f.getClass().getName());			
	}
}
 
class Foo {
	public void print() {
		System.out.println("abc");
	}
}

Producción:

myreflection.Foo

Ejemplo 2: método de invocación en un objeto desconocido

Para el ejemplo de código a continuación, se desconocen los tipos de un objeto. Al usar la reflexión, el código puede usar el objeto y averiguar si el objeto tiene un método llamado «imprimir» y luego llamarlo.

package myreflection;
import java.lang.reflect.Method;
 
public class ReflectionHelloWorld {
	public static void main(String[] args){
		Foo f = new Foo();
 
		Method method;
		try {
			method = f.getClass().getMethod("print", new Class<?>[0]);
			method.invoke(f);
		} catch (Exception e) {
			e.printStackTrace();
		}			
	}
}
 
class Foo {
	public void print() {
		System.out.println("abc");
	}
}
abc

Ejemplo 3: crear un objeto a partir de una instancia de clase

package myreflection;
 
public class ReflectionHelloWorld {
	public static void main(String[] args){
		//create instance of "Class"
		Class<?> c = null;
		try{
			c=Class.forName("myreflection.Foo");
		}catch(Exception e){
			e.printStackTrace();
		}
 
		//create instance of "Foo"
		Foo f = null;
 
		try {
			f = (Foo) c.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}	
 
		f.print();
	}
}
 
class Foo {
	public void print() {
		System.out.println("abc");
	}
}

Ejemplo 4: obtener el constructor y crear una instancia

package myreflection;
 
import java.lang.reflect.Constructor;
 
public class ReflectionHelloWorld {
	public static void main(String[] args){
		//create instance of "Class"
		Class<?> c = null;
		try{
			c=Class.forName("myreflection.Foo");
		}catch(Exception e){
			e.printStackTrace();
		}
 
		//create instance of "Foo"
		Foo f1 = null;
		Foo f2 = null;
 
		//get all constructors
		Constructor<?> cons[] = c.getConstructors();
 
		try {
			f1 = (Foo) cons[0].newInstance();
			f2 = (Foo) cons[1].newInstance("abc");
		} catch (Exception e) {
			e.printStackTrace();
		}	
 
		f1.print();
		f2.print();
	}
}
 
class Foo {
	String s; 
 
	public Foo(){}
 
	public Foo(String s){
		this.s=s;
	}
 
	public void print() {
		System.out.println(s);
	}
}

Producción:

null
abc

Además, puede usar la instancia de Class para obtener interfaces implementadas, superclase, campo declarado, etc.

Ejemplo 5: cambiar el tamaño de la matriz mediante la reflexión

package myreflection;
 
import java.lang.reflect.Array;
 
public class ReflectionHelloWorld {
	public static void main(String[] args) {
		int[] intArray = { 1, 2, 3, 4, 5 };
		int[] newIntArray = (int[]) changeArraySize(intArray, 10);
		print(newIntArray);
 
		String[] atr = { "a", "b", "c", "d", "e" };
		String[] str1 = (String[]) changeArraySize(atr, 10);
		print(str1);
	}
 
	// change array size
	public static Object changeArraySize(Object obj, int len) {
		Class<?> arr = obj.getClass().getComponentType();
		Object newArray = Array.newInstance(arr, len);
 
		//do array copy
		int co = Array.getLength(obj);
		System.arraycopy(obj, 0, newArray, 0, co);
		return newArray;
	}
 
	// print
	public static void print(Object obj) {
		Class<?> c = obj.getClass();
		if (!c.isArray()) {
			return;
		}
 
		System.out.println("nArray length: " + Array.getLength(obj));
 
		for (int i = 0; i < Array.getLength(obj); i++) {
			System.out.print(Array.get(obj, i) + " ");
		}
	}
}

Producción:

Array length: 10
1 2 3 4 5 0 0 0 0 0 
Array length: 10
a b c d e null null null null null 

Resumen

Los ejemplos de código anteriores muestran un conjunto muy pequeño de funciones proporcionadas por la reflexión de Java. La lectura de esos ejemplos solo puede darle una idea de la reflexión de Java, es posible que desee Lea más información en el sitio web de Oracle.

Referencias:

1. http://en.wikipedia.org/wiki/Reflection_(computer_programming)
2. http://docs.oracle.com/javase/tutorial/reflect/

Categorías
Diagram Memory

La introducción de las fugas de memoria de Java

Una de las ventajas más importantes de Java es su gestión de memoria. Simplemente crea objetos y Java Garbage Collector se encarga de asignar y liberar memoria. Sin embargo, la situación no es tan simple, porque las pérdidas de memoria ocurren con frecuencia en las aplicaciones Java.

Este tutorial ilustra qué es la pérdida de memoria, por qué ocurre y cómo prevenirla.

1. ¿Qué es la pérdida de memoria?

Definicion de Pérdida de memoria: la aplicación ya no utiliza los objetos, pero Garbage Collector no puede eliminarlos porque se hace referencia a ellos.

Para comprender esta definición, necesitamos comprender el estado de los objetos en la memoria. El siguiente diagrama ilustra lo que no se usa y lo que no se hace referencia.

Del diagrama, hay objetos referenciados y objetos sin referencia. Los objetos sin referencia serán recolectados como basura, mientras que los objetos referenciados no serán recolectados como basura. Los objetos no referenciados seguramente no se utilizan, porque ningún otro objeto se refiere a ellos. Sin embargo, no todos los objetos no utilizados están sin referencia. ¡Algunos de ellos están siendo referenciados! De ahí es de donde vienen las pérdidas de memoria.

2. ¿Por qué ocurren las pérdidas de memoria?

Echemos un vistazo al siguiente ejemplo y veamos por qué ocurren las pérdidas de memoria. En el siguiente ejemplo, el objeto A se refiere al objeto B. La vida útil de A (t1 – t4) es mucho más larga que la de B (t2 – t3). Cuando B ya no se usa en la aplicación, A todavía tiene una referencia a él. De esta forma, Garbage Collector no puede eliminar B de la memoria. Esto posiblemente causaría un problema de falta de memoria, porque si A hace lo mismo con más objetos, entonces habría muchos objetos que no se recopilarían y consumirían espacio en la memoria.

También es posible que B contenga un montón de referencias de otros objetos. Los objetos a los que hace referencia B tampoco se recopilarán. Todos esos objetos no utilizados consumirán un valioso espacio de memoria.

tiempo de vida del objeto

3. ¿Cómo prevenir pérdidas de memoria?

Los siguientes son algunos consejos prácticos rápidos para prevenir pérdidas de memoria.

  1. Preste atención a las clases de Colección, como HashMap, ArrayList, etc., ya que son lugares comunes para encontrar fugas de memoria. Cuando se declaran static, su tiempo de vida es el mismo que el tiempo de vida de la aplicación.
  2. Preste atención a los oyentes de eventos y las devoluciones de llamada. Puede producirse una pérdida de memoria si se registra un oyente pero no se anula el registro cuando la clase ya no se utiliza.
  3. «Si una clase administra su propia memoria, el programador debe estar alerta a las pérdidas de memoria».[1] A menudo, las variables miembro de un objeto que apuntan a otros objetos deben ser nulas.

4. Un pequeño cuestionario: ¿Por qué el método substring () en JDK 6 puede causar pérdidas de memoria?

Para responder a esta pregunta, es posible que desee leer Substring () en JDK 6 y 7.

Referencias:
[1] Bloch, Joshua. Java efectivo. Addison-Wesley Professional, 2008.
[2] Trabajo de desarrollador de IBM. http://www.ibm.com/developerworks/library/j-leaks/