sirve - static final java
¿El uso de las variables finales en Java mejora la recolección de basura? (14)
Hoy, mis colegas y yo tenemos una discusión sobre el uso de la palabra clave final
en Java para mejorar la recolección de basura.
Por ejemplo, si escribe un método como:
public Double doCalc(final Double value)
{
final Double maxWeight = 1000.0;
final Double totalWeight = maxWeight * value;
return totalWeight;
}
Declarar las variables en el método final
ayudaría a la recolección de basura a limpiar la memoria de las variables no utilizadas en el método una vez que el método haya finalizado.
¿Es esto cierto?
Algunos puntos para aclarar:
Anular la referencia no debería ayudar a GC. Si lo hiciera, indicaría que sus variables tienen un alcance mayor. Una excepción es el caso del nepotismo de objetos.
No hay asignación en la pila hasta el momento en Java.
Declarar una variable final significa que no puede (en condiciones normales) asignar un nuevo valor a esa variable. Dado que el final no dice nada sobre el alcance, no dice nada sobre su efecto en el GC.
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 creas una instancia de MyClass, crearás una referencia saliente a una instancia de MyOtherObject, y el GC tendrá que seguir ese enlace para buscar objetos en vivo.
La JVM usa un algoritmo GC marca-sweep, que tiene que examinar todos los refereces en vivo en las ubicaciones "raíz" del GC (como todos los objetos en la pila de llamadas actual). Cada objeto vivo está "marcado" como vivo, y cualquier objeto al que se refiera un objeto vivo también se marca como vivo.
Una vez completada la fase de marca, el GC recorre el montón, liberando la memoria para todos los objetos no marcados (y compactando la memoria para los objetos vivos restantes).
Además, es importante reconocer que la memoria Heap de Java está dividida en una "generación joven" y una "generación anterior". Todos los objetos se asignan inicialmente en la generación joven (a veces denominada "guardería"). Dado que la mayoría de los objetos son de corta duración, el GC es más agresivo sobre la liberación de 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 la "generación permanente"), que se procesa con menos frecuencia.
Entonces, fuera 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 su gestión de memoria en Java es eliminar referencias espurias lo más rápido posible. Usted puede hacer eso asignando "nulo" a una referencia de objeto tan pronto como termine de usarlo.
O, mejor aún, minimice el tamaño de cada ámbito de declaración. Por ejemplo, si declara un objeto al principio de un método de 1000 líneas, y si el objeto permanece activo hasta el cierre del alcance de ese método (el último corchete de cierre), entonces el objeto puede permanecer vivo durante mucho más tiempo que en realidad necesario.
Si utiliza métodos pequeños, con solo una docena de líneas de código, entonces los objetos declarados dentro de ese método quedará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 se muevan a la generación anterior a menos que sea absolutamente necesario.
Bueno, no sé sobre el uso del modificador "final" en este caso, o su efecto en el GC.
Pero puedo decirle esto: su uso de valores en caja en lugar de primitivos (por ejemplo, doble en lugar de doble) asignará esos objetos en el montón en lugar de la pila, y producirá basura innecesaria que el GC tendrá que limpiar.
Solo uso primitivos en caja cuando lo requiere una API existente, o cuando necesito primativos que se pueden anular.
Declarar una variable local final
no afectará la recolección de basura, solo significa que no puede modificar la variable. Su ejemplo anterior no debe compilarse ya que está modificando la variable totalWeight
que se ha marcado como final
. Por otro lado, declarar una voluntad final
primitiva ( double
vez de Double
) permite que esa variable se inserte en el código de llamada, por lo que podría causar cierta mejora en la memoria y el rendimiento. Esto se usa cuando tienes un número de public static final Strings
en una clase.
En general, el compilador y el tiempo de ejecución optimizarán donde sea posible. Lo mejor es escribir el código de forma adecuada y no tratar de ser demasiado complicado. Use el final
cuando no quiera que se modifique la variable. Asuma que el compilador realizará cualquier optimización fácil, y si le preocupa el rendimiento o el uso de la memoria, use un generador de perfiles para determinar el problema real.
GC actúa en refs inalcanzables. Esto no tiene nada que ver con "final", que es simplemente una afirmación de asignación de una sola vez. ¿Es posible que el GC de alguna VM pueda hacer uso de "final"? No veo cómo ni por qué.
Hay un caso de esquina no tan conocido con los recolectores de basura generacionales. (Para una breve descripción lea la respuesta de benjismith para una visión más profunda lea los artículos al final).
La idea en GC generacionales es que la mayoría de las veces solo se deben considerar las generaciones jóvenes. La ubicación raíz se escanea en busca de referencias y luego se escanean los objetos de la generación joven. Durante estos barridos más frecuentes no se verifica ningún objeto en la generación anterior.
Ahora, el problema proviene del hecho de que un objeto no puede tener referencias a objetos más jóvenes. Cuando un objeto de larga duración (generación anterior) obtiene una referencia a un nuevo objeto, el recolector de basura debe rastrear esa referencia explícitamente (consulte el artículo de IBM en el recopilador de JVM del punto de acceso ), lo que afecta realmente el rendimiento del GC.
La razón por la cual un objeto viejo no puede referirse a uno más joven es que, como el objeto antiguo no se verifica en colecciones menores, si la única referencia al objeto se guarda en el objeto anterior, no se marcará, y sería erróneo desasignado durante la etapa de barrido.
Por supuesto, como señalaron muchos, la palabra clave final no afecta realmente al recolector de basura, pero garantiza que la referencia nunca se cambiará a un objeto más joven si este objeto sobrevive a las colecciones menores y lo hace al montón anterior.
Artículos:
IBM en la recolección de basura: history , en el punto de acceso JVM y performance . Es posible que ya no sean completamente válidos, ya que datan de 2003/04, pero ofrecen una visión fácil de leer sobre los GC.
Sun on Tuning recolección de basura
La única vez que prefiero declarar las variables locales como definitivas es cuando:
Tengo que hacer que sean definitivas para que puedan ser compartidas con alguna clase anónima (por ejemplo: crear subproceso de daemon y dejar que acceda a algún valor del método adjunto)
Quiero que sean definitivos (por ejemplo: algún valor que no debería / no debe anularse por error)
¿Ayudan en la recolección rápida de basura?
AFAIK un objeto se convierte en un candidato de la colección de GC si no tiene referencias fuertes y, en ese caso, tampoco hay garantía de que se recolectarán inmediatamente. En general, se dice que una referencia fuerte muere cuando se sale del alcance o el usuario la reasigna explícitamente a una referencia nula, por lo tanto, declararla como definitiva significa que la referencia continuará existiendo hasta que el método exista (a menos que su alcance se reduzca explícitamente a un bloque interno específico {}) porque no puede reasignar las variables finales. Así que creo que wrt Garbage Collection ''final'' puede introducir un posible retraso no deseado.
Las variables finales no se pueden cambiar después de la asignación inicial (impuesta por el compilador).
Esto no cambia el comportamiento de la recolección de basura como tal. Lo único es que estas variables no se pueden anular cuando ya no se usan (lo que puede ayudar a la recolección de basura en situaciones de memoria ajustada).
Debe saber que la final le permite al compilador hacer suposiciones sobre qué optimizar. Código de entrada y no incluye código conocido que no es alcanzable.
final boolean debug = false;
......
if (debug) {
System.out.println("DEBUG INFO!");
}
La impresión no se incluirá en el código de bytes.
Lo único que se me ocurre es que el compilador puede optimizar las variables finales y alinearlas como constantes en el código, por lo que termina sin memoria asignada.
No, no es enfáticamente cierto.
Recuerde que final
no significa constante, simplemente significa que no puede cambiar la referencia.
final MyObject o = new MyObject();
o.setValue("foo"); // Works just fine
o = new MyObject(); // Doesn''t work.
Puede haber una pequeña optimización basada en el conocimiento de que la JVM nunca tendrá que modificar la referencia (como no tener control para ver si ha cambiado), pero sería tan leve como para no preocuparse.
Final
debe considerarse un metadato útil para el desarrollador y no una optimización del compilador.
Parece que hay muchas respuestas que son conjeturas errantes. La verdad es que no hay un modificador final para las variables locales en el nivel de bytecode. La máquina virtual nunca sabrá que sus variables locales se definieron como definitivas o no.
La respuesta a su pregunta es un enfático no.
Todos los métodos y variables pueden anularse por defecto en las subclases. Si queremos guardar las subclases para que no se sobrepasen los miembros de la superclase, podemos declararlas como definitivas utilizando la palabra clave final. Por ejemplo, final int a=10;
final void display(){......}
Hacer que un método sea definitivo garantiza que la funcionalidad definida en la superclase nunca se modifique de todos modos. Del mismo modo, el valor de una variable final nunca se puede cambiar. Las variables finales se comportan como variables de clase.
absolutamente, siempre y cuando la vida del objeto sea más corta, lo que produce un gran beneficio de la gestión de la memoria, recientemente examinamos la funcionalidad de exportación que tiene variables de instancia en una prueba y otra prueba que tiene una variable local de nivel de método. durante la prueba de carga, JVM arroja el error de memoria en la primera prueba y se detiene la JVM. pero en la segunda prueba, con éxito puede obtener el informe debido a una mejor gestión de la memoria.
final
en variables locales y parámetros no hace ninguna diferencia en los archivos de clase producidos, por lo que no puede afectar el rendimiento del tiempo de ejecución. Si una clase no tiene subclases, HotSpot trata esa clase como si fuera definitiva de todos modos (puede deshacer más adelante si se carga una clase que rompe esa suposición). Creo que final
en los métodos es muy similar a las clases. final
en el campo estático puede permitir que la variable se interprete como una "constante de tiempo de compilación" y la optimización que debe realizar javac sobre esa base. final
en los campos le permite a la JVM cierta libertad para ignorar las relaciones de pase previo .