java - tutorial - ¿Una expresión lambda crea un objeto en el montón cada vez que se ejecuta?
linq lambda c# (3)
Cuando una instancia que representa el lambda se crea con sensibilidad depende del contenido exacto del cuerpo de su lambda. A saber, el factor clave es lo que la lambda captura del entorno léxico. Si no captura ningún estado que sea variable desde la creación hasta la creación, entonces no se creará una instancia cada vez que se ingrese el bucle for-each. En su lugar, se generará un método sintético en tiempo de compilación y el sitio de uso lambda solo recibirá un objeto singleton que delegue en ese método.
Además, tenga en cuenta que este aspecto depende de la implementación y puede esperar refinamientos y avances futuros en HotSpot hacia una mayor eficiencia. Hay planes generales, por ejemplo, para hacer un objeto liviano sin una clase correspondiente completa, que tiene la información suficiente para reenviar a un único método.
Aquí hay un buen y accesible artículo en profundidad sobre el tema:
Cuando itero sobre una colección usando el nuevo azúcar sintáctico de Java 8, como
myStream.forEach(item -> {
// do something useful
});
¿No es esto equivalente al fragmento de ''vieja sintaxis'' a continuación?
myStream.forEach(new Consumer<Item>() {
@Override
public void accept(Item item) {
// do something useful
}
});
¿Significa esto que se crea un nuevo objeto de Consumer
anónimo en el montón cada vez que iterar sobre una colección? ¿Cuánto espacio de almacenamiento ocupa? ¿Qué implicaciones de rendimiento tiene? ¿Significa que debería usar el estilo anterior para bucles cuando itere sobre estructuras de datos de varios niveles?
Es equivalente pero no idéntico. Dicho simplemente, si una expresión lambda no captura valores, será un singleton que se reutilizará en cada invocación.
El comportamiento no está exactamente especificado. La JVM tiene una gran libertad para implementarla. Actualmente, la JVM de Oracle crea (al menos) una instancia por expresión lambda (es decir, no comparte instancia entre diferentes expresiones idénticas) pero crea singleton para todas las expresiones que no capturan valores.
Puede leer esta respuesta para más detalles. Allí, no solo proporcioné una descripción más detallada sino también un código de prueba para observar el comportamiento actual.
Esto está cubierto por The Java® Language Specification, capítulo " 15.27.4. Evaluación en tiempo de ejecución de Lambda Expressions "
Resumido:
Estas reglas están destinadas a ofrecer flexibilidad a las implementaciones del lenguaje de programación Java, en que:
No es necesario asignar un objeto nuevo en cada evaluación.
Los objetos producidos por diferentes expresiones lambda no necesitan pertenecer a clases diferentes (si los cuerpos son idénticos, por ejemplo).
Cada objeto producido por evaluación no necesita pertenecer a la misma clase (las variables locales capturadas pueden estar en línea, por ejemplo).
Si hay una "instancia existente" disponible, no es necesario que se haya creado en una evaluación lambda anterior (por ejemplo, podría haber sido asignada durante la inicialización de la clase adjunta).
Está pasando una nueva instancia al método forEach
. Cada vez que lo haces, creas un objeto nuevo, pero no uno por cada iteración de bucle. La iteración se realiza dentro de forEach
método utilizando la misma instancia de objeto de "devolución de llamada" hasta que se completa con el ciclo.
Entonces, la memoria utilizada por el ciclo no depende del tamaño de la colección.
¿No es esto equivalente al fragmento de "vieja sintaxis"?
Sí. Tiene ligeras diferencias a un nivel muy bajo, pero no creo que deba preocuparse por ellas. Las expresiones de Lamba utilizan la función invocada dinámica en lugar de las clases anónimas.