2014-09-04 12 views
13

In golang, sembra che non ci siano costruttori, ma è consigliabile allocare un oggetto di tipo struct usando una funzione, solitamente chiamata "New" + TypeName, ad esempioLayout di memoria Golang rispetto a C++/C

func NewRect(x,y, width, height float) *Rect { 
    return &Rect(x,y,width, height) 
} 

Tuttavia, non sono sicuro del layout di memoria di Go. In C/C++, questo tipo di codice significa che si restituisce un puntatore, che punta a un oggetto temporaneo perché la variabile è allocata nello stack e la variabile potrebbe essere un cestino dopo il ritorno della funzione. In Golang, devo preoccuparmi di questo genere di cose? Perché Sembra che nessuno standard dimostri che tipo di dati saranno allocati nello stack e quale tipo di dati saranno allocati nell'heap.

Come in Java, sembra esserci un punto specifico che il tipo di base come int, float verrà allocato nello stack, l'altro oggetto derivato dall'oggetto verrà allocato nello heap. In golang, c'è un discorso specifico su questo?

+5

Sarà assegnato sullo heap. Il compilatore Go può rilevare quando un oggetto vive all'esterno dello stack e lo assegna automaticamente all'heap. Puoi vederlo se compili con 'go build -gcflags '-m'' per vedere le decisioni di ottimizzazione. – siritinga

+2

@python: il tuo intuito era giusto che non ti devi preoccupare di questo. In pratica è vero che sarà assegnato all'heap, ma il modello di memoria di Go è molto semplice, e in effetti non devi pensare a pile e pile. Puoi solo pensare alle variabili. Se si prende l'indirizzo di qualcosa, Go garantirà che quell'indirizzo è sempre valido fino a quando si ha un puntatore. Non dovrebbe importarti come programmatore come ciò accade. (anche se, ovviamente, può essere interessante pensare a come è implementato, e ci sono cose interessanti anche lì) – joshlf

+0

grazie per tutti i tuoi ragazzi rispondi allo – python

risposta

26

Il Composite Literal section menzioni:

Prendendo l'indirizzo di un letterale composito (operatori §Address) genera un puntatore unico a un'istanza del valore del letterale.

Ciò significa che il puntatore restituito dalla funzione New sarà valido (assegnato nello stack).
Calls:

In una chiamata di funzione, il valore della funzione e gli argomenti sono valutate nell'ordine usuale.
Dopo la valutazione, i parametri della chiamata vengono passati di valore alla funzione e la funzione chiamata inizia l'esecuzione.
I parametri di ritorno della funzione vengono passati di nuovo dalla funzione di chiamata quando la funzione restituisce.

Puoi vedere di più in this answer e this thread.

Come accennato in "Stack vs heap allocation of structs in Go, and how they relate to garbage collection":

Vale la pena notare che le parole 'stack' e 'heap' non appaiono da nessuna parte nelle specifiche lingua.


I dettagli blog post "Escape Analysis in Go" ciò accade, mentioning the FAQ:

Quando possibile, i compilatori Go assegnerà variabili che sono locali a una funzione in frame dello stack di quella funzione.
Tuttavia, se il compilatore non è in grado di dimostrare che la variabile non viene referenziata dopo che la funzione è stata restituita, il compilatore deve allocare la variabile nell'heap garbage-collected per evitare errori di puntatore pendenti.
Inoltre, se una variabile locale è molto grande, potrebbe avere più senso archiviarla nell'heap piuttosto che nello stack.

Il post sul blog aggiunge:

Il codice che fa il “fuga analisi” vive in src/cmd/gc/esc.c.
Concettualmente, tenta di determinare se una variabile locale sfugge all'ambito corrente; gli unici due casi in cui ciò accade sono quando viene restituito l'indirizzo di una variabile e quando il suo indirizzo è assegnato a una variabile in un ambito esterno.
Se una variabile esegue l'escape, deve essere allocata nell'heap; altrimenti, è sicuro metterlo in pila.

È interessante notare che questo vale anche per le allocazioni new(T).
Se non scappano, finiranno per essere allocato nello stack. Ecco un esempio per chiarire le cose:

var intPointerGlobal *int = nil 

func Foo() *int { 
    anInt0 := 0 
    anInt1 := new(int) 

    anInt2 := 42 
    intPointerGlobal = &anInt2 

    anInt3 := 5 

    return &anInt3 
} 

Sopra, anInt0 e anInt1 non sfuggono, in modo che siano allocate sullo stack;
anInt2 e anInt3 escape e sono allocate nell'heap.


Vedere anche "Five things that make Go fast":

A differenza di C, che ti costringe a scegliere se un valore verrà memorizzato sul mucchio, tramite malloc, o in pila, dichiarandolo all'interno della portata della funzione, Go implementa un'ottimizzazione chiamata analisi di fuga.

Le ottimizzazioni di Go sono sempre abilitate per impostazione predefinita.
È possibile visualizzare l'analisi di escape del compilatore e prendere in considerazione le decisioni con lo switch -gcflags=-m.

Poiché l'analisi di escape viene eseguita in fase di compilazione, non in fase di esecuzione, l'allocazione dello stack sarà sempre più veloce dell'allocazione dell'heap, indipendentemente dall'efficienza del garbage collector.

+1

Anche se la specifica non lo menziona, il compilatore esegue l'analisi di escape: './test.go: 11: new (int) escapes to heap' – siritinga

+0

@siritinga Infatti. Ho modificato la risposta con ulteriori dettagli sull'analisi di escape, provenienti da http://stackoverflow.com/questions/13715237/return-pointer-to-local-struct#comment23436456_13715281 – VonC

+0

, era solo un commento. Alla fine non è così facile evitare di usare termini heap/stack :) – siritinga

Problemi correlati