Aquí hay un ejemplo ligeramente diferente, uno con campos de tipo de referencia final en lugar de variables locales de tipo de valor final:
public class MyClass {
public final MyOtherObject obj;
}
Cada vez que crea una instancia de MyClass, creará una referencia saliente a una instancia de MyOtherObject, y el GC tendrá que seguir ese enlace para buscar objetos activos.
La JVM utiliza un algoritmo GC de barrido de marca, que tiene que examinar todas las referencias en vivo en las ubicaciones "raíz" de GC (como todos los objetos en la pila de llamadas actual). Cada objeto vivo se "marca" como vivo, y cualquier objeto al que se refiere un objeto vivo también se marca como vivo.
Después de la finalización de la fase de marca, el GC barre el montón, liberando memoria para todos los objetos sin marcar (y compactando la memoria para los objetos vivos restantes).
Además, es importante reconocer que la memoria del montón de Java está dividida en una "generación joven" y una "generación anterior". Todos los objetos se asignan inicialmente a la generación joven (a veces denominada "la guardería"). Dado que la mayoría de los objetos son de corta duración, el GC es más agresivo en cuanto a liberar la basura reciente de la generación joven. Si un objeto sobrevive a un ciclo de recolección de la generación joven, se traslada a la generación anterior (a veces denominada "generación titular"), que se procesa con menos frecuencia.
Entonces, en lo alto de mi cabeza, voy a decir "no, el modificador 'final' no ayuda al GC a reducir su carga de trabajo".
En mi opinión, la mejor estrategia para optimizar la gestión de la memoria en Java es eliminar las referencias falsas lo más rápido posible. Puede hacerlo asignando "nulo" a una referencia de objeto tan pronto como haya terminado de usarlo.
O, mejor aún, minimice el tamaño del alcance de cada declaración. Por ejemplo, si declara un objeto al comienzo de un método de 1000 líneas, y si el objeto permanece vivo hasta el cierre del alcance de ese método (la última llave de cierre), entonces el objeto podría permanecer vivo por mucho más tiempo que en realidad necesario.
Si usa métodos pequeños, con solo una docena de líneas de código, entonces los objetos declarados dentro de ese método caerán fuera del alcance más rápidamente, y el GC podrá hacer la mayor parte de su trabajo dentro de la mucho más eficiente generación joven. No desea que los objetos pasen a la generación anterior a menos que sea absolutamente necesario.