Normalmente, la parte runtime dell'implementazione lambda genererà una classe che consiste essenzialmente in un singolo metodo di implementazione. Le informazioni necessarie per generare tale classe sono date da un richiamo del metodo bootstrap a LambdaMetafactory.metafactory
in fase di runtime.
Quando si abilita la serializzazione, le cose si complicano. Innanzitutto, il codice compilato utilizzerà il metodo di bootstrap alternativo LambdaMetafactory.altMetafactory
che offre una maggiore flessibilità al prezzo di dover analizzare i parametri varargs in base ai flag specificati all'interno dell'array dei parametri.
Poi la classe lambda generata deve avere un metodo writeReplace
(vedere la seconda metà del Serializable
documentation) che deve creare e restituire un'istanza SerializedLambda
contenente tutte le informazioni necessarie per ricreare l'istanza lambda. Poiché il metodo di implementazione singola di una classe lambda consiste in una semplice chiamata di delega, il metodo writeReplace
e le relative informazioni costanti moltiplicheranno la dimensione della classe generata.
È anche interessante notare che la classe crea l'istanza lambda Serializable avrà un metodo sintetico $deserializeLambda$
(confrontare la class documentation of SerializedLambda
come controparte al processo di del lambda writeReplace
. Che aumenterà l'utilizzo classi disco e tempo di caricamento (ma non influisce sulla valutazione delle espressioni lambda).
Nel codice esempio, entrambi i metodi sarebbero influenzati dalla stessa quantità di tempo come bootstrapping e generazione classe avviene solo una volta per un'espressione lambda. In seguito a valutazioni, the class generated on the first evaluation will be re-used and only a new instance created (if not even the instance is re-used) Stiamo parlando di una volta in testa qui, anche quando l'agnello l'espressione da è contenuta in un ciclo, influisce solo sulla prima iterazione.
Si noti che se si dispone di un'espressione lambda all'interno di un ciclo, non ci potrebbe essere una nuova istanza creato per ogni iterazione, pur avendo al di fuori del ciclo sarà di sicuro avere un'istanza durante l'intero ciclo. Ma questo comportamento non dipende dalla domanda se l'interfaccia di destinazione è Serializable
. Dipende semplicemente dal fatto che l'espressione acquisisca valore (confrontare con this answer).
Nota che se avessi scritto
final long toAdd = 1;
MyFunction<Long, Long> adder = (number) -> number + toAdd;
nel secondo metodo (si noti l'esplicito final
modificatore) il valore toAdd
sarebbe una fase di compilazione costante e l'espressione viene compilata come se tu avessi scritto (number) -> number + 1
, cioè non catturerà più un valore. Quindi si otterrebbe la stessa istanza lambda in ogni ciclo iterativo (con la versione corrente di Oracle JVM). Quindi la domanda se una nuova istanza viene creata a volte dipende da piccoli frammenti del contesto. Ma di solito, l'impatto sulle prestazioni è piuttosto limitato.
Grazie per la bella spiegazione. –
Questa spiegazione non sta colpendo il problema. La deserializzazione degli oggetti utilizzerà sempre il costoso Reflection indipendentemente dal fatto che si utilizzino o meno espressioni lambda. Ma il codice della domanda non deserializza nulla. – Holger
La domanda riguardava la serializzazione e l'uso di lambda serializzabili, al contrario di altri tipi di lamdbas. OP chiedeva quale parte del ciclo di vita di quegli oggetti fosse meno performante del normale lambda. Il suo codice distingue correttamente tra invocazione e istanziazione, e la sua domanda lo chiede direttamente. Come la risposta ha indicato, uno dei suoi casi è interessato. Deserializzare lambda fa/cose molto diverse/che deserializzare altri oggetti ("riflessione speciale") ed è intrinsecamente più costoso. – BadZen