c# java android .net garbage-collection

c# - Vida útil de los objetos en Java vs.Net



android garbage-collection (2)

Java, en general, tiene el comportamiento de que si el objeto es alcanzable en el alcance (hay una referencia que no es basura), entonces el objeto no es basura. Esto es recursivo, por lo que si a es una referencia a un objeto que tiene una referencia a b , el objeto b refiere a que no es basura.

Dentro del alcance donde todavía puede alcanzar el objeto referenciado por ref , (puede agregar una línea System.out.println(ref.toString()) ), ref no es basura.

Sin embargo, de acuerdo con esta antigua fuente del sitio de Sun , la mayoría depende de la implementación específica de la JVM.

Estaba leyendo "CLR a través de C #" y parece que en este ejemplo, el objeto que se asignó inicialmente a ''obj'' será elegible para Garbage Collection después de que se ejecuta la línea 1, no después de la línea 2.

void Foo() { Object obj = new Object(); obj = null; }

Esto se debe a que la vida útil variable local no está definida por el ámbito en el que se definió, sino por la última vez que la leíste.

Entonces mi pregunta es: ¿qué hay de Java? He escrito este programa para verificar tal comportamiento, y parece que el objeto se mantiene vivo. No creo que sea posible para la JVM limitar el tiempo de vida de la variable mientras se interpreta bytecode, así que traté de ejecutar el programa con ''java -Xcomp'' para forzar la compilación de métodos, pero ''finalizar'' no se llama de todos modos. Parece que eso no es cierto para Java, pero espero que pueda obtener una respuesta más precisa aquí. Además, ¿qué pasa con la máquina virtual Dalvik de Android?

class TestProgram { public static void main(String[] args) { TestProgram ref = new TestProgram(); System.gc(); } @Override protected void finalize() { System.out.println("finalized"); } }

Agregado: Jeffrey Richter da un ejemplo de código en "CLR a través de C #", algo como esto:

public static void Main (string[] args) { var timer = new Timer(TimerCallback, null, 0, 1000); // call every second Console.ReadLine(); } public static void TimerCallback(Object o) { Console.WriteLine("Callback!"); GC.Collect(); }

TimerCallback solo se llama una vez en MS .Net si el objetivo de los proyectos es ''Release'' (temporizador destruido después de GC.Collect ()) y se llama cada segundo si el objetivo es ''Debug'' (las variables aumentan porque el programador puede intentar acceder al objeto con depurador ) Pero en la devolución de llamada Mono se llama cada segundo, sin importar cómo lo compile. Parece que la implementación de Mono "Timer" almacena referencias a la instancia en algún lugar del grupo de subprocesos. La implementación de MS no hace esto.


Tenga en cuenta que el hecho de que un objeto se pueda recopilar no significa que realmente se recopile en un punto determinado, por lo que su método puede dar falsos negativos. Si se llama al método de finalize cualquier objeto, definitivamente puede decir que era inalcanzable, pero si no se llama al método, no puede inferir lógicamente nada. Al igual que con la mayoría de las preguntas relacionadas con GC, el no determinismo del recolector de basura hace que sea difícil encontrar pruebas / garantías sobre qué hará exactamente.

Sobre el tema de accesibilidad / cobrabilidad, el JLS dice (12.6.1):

Un objeto accesible es cualquier objeto al que se pueda acceder en cualquier cómputo potencial continuo desde cualquier hilo en vivo. Se pueden diseñar transformaciones de optimización de un programa que reduzcan la cantidad de objetos que se pueden alcanzar para que sean menores que aquellos que ingenuamente se considerarían alcanzables. Por ejemplo, un compilador o generador de código puede optar por establecer una variable o parámetro que ya no se utilizará para anular para hacer que el almacenamiento de dicho objeto sea potencialmente recuperable antes.

Que es más o menos exactamente lo que esperarías, creo que el párrafo anterior es isomorfo con "un objeto no está disponible si definitivamente no lo usarás más".

Volviendo a su situación original, ¿ puede pensar en alguna ramificación práctica entre el objeto que se considera inalcanzable después de la línea 1 en comparación con la línea 2 ? Mi reacción inicial es que no hay ninguno, y si de alguna manera lograste encontrar una situación así, probablemente sería una marca de código incorrecto / retorcido que haría que el VM tuviera dificultades en lugar de una debilidad inherente en el lenguaje.

Aunque estoy abierto a contraargumentos.

Editar: Gracias por el interesante ejemplo.

Estoy de acuerdo con su evaluación y veo hacia dónde se dirige, aunque es probable que el problema sea que el modo de depuración está cambiando sutilmente la semántica de su código.

En el código tal como está escrito, asigna el Timer a una variable local que no se lee posteriormente dentro de su alcance. Incluso el análisis de escape más trivial puede revelar que las variables del timer no se utilizan en ningún otro lugar en el método main , y por lo tanto se pueden elidir. Por lo tanto, creo que su primera línea puede considerarse exactamente equivalente a invocar directamente al constructor:

public static void Main (string[] args) { new Timer(TimerCallback, null, 0, 1000); // call every second ...

En este último caso, está claro que el objeto Timer recién creado no es alcanzable inmediatamente después de la construcción (suponiendo que no haga nada furtivo como agregarse a campos estáticos, etc. en su constructor); y para que se recolectara tan pronto como el CG lo consiguiera.

Ahora, en el caso de la depuración, las cosas son sutilmente diferentes, por la razón que usted mencionó que es que el desarrollador puede desear inspeccionar el estado de las variables locales más adelante en el método. Por lo tanto, el compilador (y el compilador JIT) no pueden optimizarlos; es como si hubiera un acceso de la variable al final del método, lo que impide la recopilación hasta ese punto.

Aun así, no creo que esto realmente cambie la semántica. La naturaleza de GC es que la recopilación rara vez está garantizada (al menos en Java, la única garantía que obtienes es que si se lanzaba un OutOfMemoryError, todo lo que se consideraba inalcanzable se GCed inmediatamente antes). De hecho, suponiendo que tenía suficiente espacio en el montón para contener todos los objetos creados durante la vida útil del motor de ejecución, una implementación GC no operativa es perfectamente válida. Entonces, si bien puede observar cambios de comportamiento en la cantidad de veces que el Timer marca, esto está bien ya que no hay garantías sobre lo que verá en función de la forma en que lo está invocando. (Esto es conceptualmente similar a cómo un temporizador que se ejecuta en una tarea intensiva de CPU marcaría más veces cuando el sistema estaba bajo carga; ninguno de los resultados es incorrecto porque la interfaz no proporciona ese tipo de garantía).

En este punto, te remito a la primera oración en esta respuesta. :)