2015-02-12 1 views
1

Я пытаюсь провести эксперимент в Go, чтобы узнать, что произойдет, если я буду хранить указатель на переменную в стеке и затем получить доступ к этой переменной после того, как исходная переменная оставила область видимости ,Сохранение указателя на значение стека (Golang)

package main 

import "fmt" 

var p chan bool; 

// a temp struct 
type v struct { 
    a int 
} 

func another_thread(vx *v) { 
    // this code should be executed after a() returns so vx should be a pointer to a value that's no longer on the stack 
    fmt.Printf("another_thread(): %p\n", vx); 
    vx.a = 4 // am I updating a dangling pointer that may have unintentional side effects?? 
    fmt.Println(" - ", vx.a); 
    p<-true; 
} 

func update_v(vx *v) { 
    vx.a = 3; 

    fmt.Printf("update_v(): %p\n", vx); 

    go another_thread(vx) 
} 

func alloc_on_stack() { 
    // allocate v1 on the stack 
    var v1 v 
    v1.a = 1 

    fmt.Printf("alloc_on_stack(): %p\n", &v1); 

    // pass a pointer to v1 on the stack 
    update_v(&v1) 

    // print '3' to prove byref actually took it by reference 
    fmt.Println(" - ", v1.a); 

    // when the function returns, v1 should be popped off the stack 
} 

func main() { 
    p = make(chan bool) 
    alloc_on_stack(); 
    fmt.Println("outside of alloc_on_stack, waiting"); 
    <-p; 
    fmt.Println("done"); 
} 

В alloc_on_stack v1 хранится как локальная переменная в стеке. Я передаю указатель на v1 на update_v, который передает его другому_thread. Другим_thread не выполняется до тех пор, пока alloc_on_stack не завершится.

Тем не менее, когда я запускаю этот код, я не получаю никаких ошибок, вместо этого я вижу это:

alloc_on_stack(): 0x1043617c 
update_v(): 0x1043617c 
- 3 
outside of alloc_on_stack, waiting 
another_thread(): 0x1043617c 
- 4 
done 

Если не ое внутри another_thread быть оборванным указатель?

ответ

4

Go не различает стек и кучу как язык. В его реализациях используется анализ escape-кода, чтобы доказать, что некоторые переменные, даже если они упоминаются, можно безопасно разместить в стеке. Наивная реализация может просто разместить все переменные, на которые ссылаются в куче.

Вы можете использовать флаг -m на 6g для печати оптимизации производительности, например, когда он помещает что-то в стек или кучу.

Учитывая ваш пример:

$ go build -gcflags "-m" tmp.go 
# command-line-arguments 
./tmp.go:12: leaking param: vx 
./tmp.go:14: another_thread ... argument does not escape 
./tmp.go:16: another_thread ... argument does not escape 
./tmp.go:20: leaking param: vx 
./tmp.go:20: leaking param: vx 
./tmp.go:20: leaking param: vx 
./tmp.go:23: update_v ... argument does not escape 
./tmp.go:30: moved to heap: v1 
./tmp.go:33: &v1 escapes to heap 
./tmp.go:36: &v1 escapes to heap 
./tmp.go:33: alloc_on_stack ... argument does not escape 
./tmp.go:39: alloc_on_stack ... argument does not escape 
./tmp.go:45: make(chan bool, 0) escapes to heap 
./tmp.go:47: main ... argument does not escape 
./tmp.go:49: main ... argument does not escape 
+0

Спасибо! Я попробовал это и заметил, когда я ссылаюсь на локальную переменную, она перемещает ее в кучу. Это очищает много вещей. –

6

Неа. Компилятор Go обнаруживает, что вы берете адрес локальной переменной и держите его до тех пор, пока все ссылки на него не исчезнут. С этого момента переменная может быть собрана мусором.

Вот почему такие вещи, как это не только разрешено, это даже идиоматическое:

func foo() *Bar { 
    return &Bar{42, "frob"} 
}