2011-08-16 5 views
7

A working document che descrive lo stato di Project Lambda menziona i cosiddetti tipi SAM (metodo astratto singolo). Per quanto ne so, l'attuale proposta lambda non influenza il runtime solo il compilatore rendendo possibile la conversione automatica da espressioni lambda a questi tipi.Java - Ottimizzazione tipo SAM

Penso che in circostanze ideali le istanze di tipi SAM potrebbero essere rappresentate internamente da puntatori di funzione. Pertanto la JVM potrebbe evitare l'allocazione di memoria per queste istanze.

Mi chiedo se le moderne macchine virtuali siano in grado di fornire tale ottimizzazione.

+0

Quando si dice "puntatore di funzione", si intende puntatore semplice all'inizio del codice funzione? – ffriend

+0

@ffriend - Sì, lo so.So che ci sono alcuni problemi con questo approccio, ad esempio è possibile sincronizzare su un oggetto. Ma la JVM può fare anche altre ottimizzazioni non banali, ad esempio l'allineamento dei metodi virtuali. –

risposta

6

@ Tamás Probabilmente si dovrebbe avere una lettura di questa mailing list post di Brian Goetz:

http://mail.openjdk.java.net/pipermail/lambda-dev/2011-August/003877.html

In sostanza, l'astrazione lambda è attualmente implementato utilizzando oggetti. Tuttavia, è stato progettato per consentire realizzazioni alternative di lambda, che sarebbero "più piccole" rispetto alle istanze di classi.

Si può pensare alla situazione come simile alla funzione di autoboxing: gli interi sono inseriti in numero intero, ma hanno una rappresentazione "più piccola" (come ints).

Attualmente, i lambda devono essere racchiusi nelle istanze di tipi SAM, b/c la JVM attualmente non ha modo di rappresentare un lambda con un costrutto più piccolo. In futuro, potrebbe esserci un nuovo standard JVM che include "funzioni primitive" che potrebbero rappresentare lambda come qualcosa di diverso dagli oggetti.

Quindi, per rispondere alla tua domanda, il tipo di ottimizzazione che proponi sopra, potrebbe essere possibile, ma probabilmente verrebbe con il post-Java 8 su "funzioni primitive" anziché essere una funzione specifica dell'implementazione.

5

Non c'è niente di difficile nella conversione delle classi di metodo singolo in puntatori di funzioni, ma manca una cosa: le espressioni lambda non sono solo funzioni, sono chiusure . La differenza è che le chiusure possono catturare variabili esterne. Consideriamo ora esempio in pseudo Java:

public Adder makeAdder(double startNumber) { 
    return #{ int number -> number + startNumber} 
} 

... 

int startNumber = 5; 
Adder add5 = makeAdder(startNumber); 
add5.invoke(4); // ==> 9 

In questo esempio funzione lambda, prodotta dalla chiamata a makeAdder(), si riferisce alla variabile che è stata definita al di fuori di questo lambda. Questo è il motivo per cui si chiamano "chiusure" - sono "chiuse" sulle loro variabili libere (in questo caso - su startNumber). Per gestire tali situazioni, le chiusure devono contenere sia il puntatore a una funzione che il puntatore al suo ambiente . Quindi, ottieni una struttura dati con un metodo e almeno una variabile. Ma non è una definizione di un oggetto in OOP? Quindi qual è la ragione per creare un nuovo tipo di oggetti se puoi renderlo un'istanza di classe anonima?

Tuttavia alcune ottimizzazioni su tali classi anonime possono essere fatte. Il documento di lavoro che hai indicato menziona alcuni di essi, ad esempio, inferendo e utilizzando efficacemente le variabili finali (sebbene ciò avvenga principalmente per consentire lambdas su JVM in principal, non per ottimizzare il codice). Anche la classe anonima prodotta può essere eseguita in modo definitivo e la maggior parte delle JVM ha già buone ottimizzazioni per le classi e le classi finali.

Altri miglioramenti possono riguardare anche i riferimenti all'ambiente - ci sono tonnellate di opzioni lì.

+0

Grazie per la risposta! L'unica cosa che mi preoccupa è l'allocazione di memoria per questi oggetti, che può limitare la concorrenza, dal momento che per quanto ne so ogni allocazione di memoria implica un numero di sincronizzazioni. Naturalmente so che gestire gli oggetti riutilizzabili è responsabilità del programmatore, volevo solo sapere se la JVM è in grado di evitare l'allocazione di memoria per questi oggetti. –

+1

@ Tamás: puoi indicare le risorse sulla sincronizzazione durante l'allocazione della memoria? Non riesco a trovare alcuna informazione su questo argomento, ma credo che l'allocazione di memoria predefinita in JVM non usi cose come 'synchronized': l'allocazione a un thread nelle VM moderne richiede solo un'assegnazione al riferimento e un incremento del puntatore. Credo che la sicurezza dei thread nell'allocazione della memoria multi-threaded sia fatta da alcuni costrutti di basso livello, probabilmente basati su operazioni atomiche del processore, e quindi anche molto veloci. Se è così, non c'è motivo di preoccuparsi della sincronizzazione durante la creazione della chiusura. – ffriend

+3

@ Tamás Hotspot utilizza internamente i buffer TLS (thread local storage) e solo se questi sono allocati completi sull'heap globale, il che può comportare un sovraccarico di sincronizzazione, ma non è ancora così costoso - è solo un incremento puntatore dopo tutto nel caso normale (Non lo so, ma questo è quasi certamente implementato con un ciclo CAS). – Voo