Vida útil de los objetos en Java vs .Net

Yo estaba leyendo "CLR a través de C #" y parece que en este ejemplo, el objeto que fue asignado inicialmente a 'obj' será elegible para Garbage Collection después de que la línea 1 se ejecute, 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 se define por el ámbito en el que se definió, sino por la última vez que lo lee.

Así que mi pregunta es: ¿qué pasa con Java? He escrito este programa para comprobar tal comportamiento, y parece que el objeto permanece vivo. No creo que sea posible que la JVM limite la vida útil variable al interpretar bytecode, así que intenté ejecutar el programa con 'java -Xcomp' para forzar la compilación del método, pero 'finalize' no se llama de todos modos. Parece que no es cierto para Java, pero espero poder obtener una respuesta más precisa aquí. Además, ¿qué hay de la VM de Dalvik para Android?

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

Añadido: Jeffrey Richter da ejemplo de código en "CLR vía C #", algo así:

 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 llamado sólo una vez en MS .Net si el objetivo de los proyectos es 'Release' (temporizador destruido después de la llamada GC.Collect ()), y llamó cada segundo si el objetivo es 'Debug' (aumento de la vida de las variables porque el programador puede intentar acceder al objeto con depurador ). Pero en Mono callback llamado cada segundo no importa cómo lo compila. Parece que la implementación de Timer de Mono hace referencia a la instancia en algún lugar del grupo de subprocesos. La implementación de MS no lo hace.

Tenga en cuenta que sólo porque un objeto puede ser recolectado, no significa que realmente se recogerá un punto determinado – por lo que su método puede dar falsos negativos. Si el método de finalize cualquier objeto se llama, definitivamente se puede decir que fue inaccesible, pero si el método no se llama, no se puede deducir lógicamente nada. Al igual que con la mayoría de las preguntas relacionadas con el GC, el no determinismo del recolector de basura hace que sea difícil llegar a pruebas / garantías sobre exactamente lo que va a hacer.

En el tema de accesibilidad / recolección, el JLS dice (12.6.1):

Un objeto accesible es cualquier objeto al que se pueda acceder en cualquier cálculo continuo potencial desde cualquier subproceso activo. Las transformaciones de optimización de un programa se pueden diseñar que reducen el número de objetos que son accesibles para ser menos que los que ingenuamente se consideran accesibles. Por ejemplo, un compilador o generador de código puede elegir establecer una variable o parámetro que ya no se utilizará para null para hacer que el almacenamiento de dicho objeto sea potencialmente recuperable antes.

Cuál es más o menos exactamente lo que usted esperaría – pienso que el párrafo antedicho es isomorfo con "un objeto es unreachable si usted definitivamente no lo utilizará 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 se las arregló para encontrar una situación así, probablemente sería una marca de código malo / retorcido que causa la VM a la lucha en lugar de una debilidad inherente en el idioma.

Aunque estoy abierto a contra-argumentos.


Edit: Gracias por el interesante ejemplo.

Estoy de acuerdo con su evaluación y ver a dónde va, aunque el problema es probablemente más que el modo de depuración está cambiando sutilmente la semántica de su código.

En el código tal como está escrito, se asigna el Timer a una variable local que no se lea posteriormente dentro de su ámbito. 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 puede eliminar. Por lo tanto, creo que su primera línea se puede considerar exactamente equivalente a sólo 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 accesible inmediatamente después de la construcción (suponiendo que no haga nada astuto, como agregarse a los campos estáticos, etc. en su constructor); Y para que se recolectara tan pronto como el GC se diera la vuelta.

Ahora en el caso de depuración las cosas son sutilmente diferentes, por la razón que ha mencionado 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 optimizar estos ausentes; Es como si hubiera un acceso de la variable al final del método, evitando la recolección hasta ese punto.

Aún así, no creo que esto cambie realmente la semántica. La naturaleza de GC es que la recolección es raramente garantizada (en Java por lo menos, la única garantía que obtienes es que si se lanzó un OutOfMemoryError entonces todo lo que se consideraba inaccesible se GCed inmediatamente de antemano). De hecho, suponiendo que tuvieras suficiente espacio en la pila para contener todos los objetos creados durante la vida útil del tiempo de ejecución, una implementación de GC sin operaciones es perfectamente válida. Por lo tanto, si bien puede observar cambios de comportamiento en cuántas veces marca el Timer , 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 a lo largo de una tarea de uso intensivo de CPU marcaría más veces cuando el sistema estaba bajo carga – ni el resultado es incorrecto porque la interfaz no proporciona ese tipo de garantía).

En este punto le remito de nuevo a la primera oración en esta respuesta. 🙂

Java, en general, tiene el comportamiento de que si el objeto es accesible en el ámbito (hay una referencia a él 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 no es basura.

Dentro del ámbito en el que todavía se puede llegar al objeto al que hace referencia ref , (se podría añadir 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.

  • Android: para medir el tiempo entre los dos clics de botón
  • Utilizar Xlint: depreciación con android
  • Cómo hacer espacio entre spannable cadena en Android?
  • Cómo quitar una tarea de ScheduledExecutorService?
  • Dagger 2: @ Component.Builder falta setters para los módulos o componentes necesarios: `
  • Problemas al importar android.support.v7.widget.CardView en Eclipse
  • Android scrollview quitar la luz azul
  • La barra de desplazamiento desaparece cuando se utiliza SectionIndexer en secciones específicas de HoneyComb
  • Compostabilidad de FutureTask de Java
  • Ejemplo androide del presentador de la versión / ejemplos del regulador
  • Conexión Bluetooth de Android: falla en la detección del servicio
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.