| // run | |
| // Copyright 2018 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package main | |
| import "runtime" | |
| var finalized bool | |
| var err string | |
| type HeapObj [8]int64 | |
| const filler int64 = 0x123456789abcdef0 | |
| func (h *HeapObj) init() { | |
| for i := 0; i < len(*h); i++ { | |
| h[i] = filler | |
| } | |
| } | |
| func (h *HeapObj) check() { | |
| for i := 0; i < len(*h); i++ { | |
| if h[i] != filler { | |
| err = "filler overwritten" | |
| } | |
| } | |
| } | |
| type StackObj struct { | |
| h *HeapObj | |
| } | |
| func gc(shouldFinalize bool) { | |
| runtime.GC() | |
| runtime.GC() | |
| runtime.GC() | |
| if shouldFinalize != finalized { | |
| err = "heap object finalized at the wrong time" | |
| } | |
| } | |
| func main() { | |
| var s StackObj | |
| s.h = new(HeapObj) | |
| s.h.init() | |
| runtime.SetFinalizer(s.h, func(h *HeapObj) { | |
| finalized = true | |
| }) | |
| gc(false) | |
| h := g(&s) | |
| gc(false) | |
| h.check() | |
| gc(true) // finalize here, after return value's last use. (Go1.11 never runs the finalizer.) | |
| if err != "" { | |
| panic(err) | |
| } | |
| } | |
| func g(p *StackObj) (v *HeapObj) { | |
| gc(false) | |
| v = p.h // last use of the stack object. the only reference to the heap object is in the return slot. | |
| gc(false) | |
| defer func() { | |
| gc(false) | |
| recover() | |
| gc(false) | |
| }() | |
| *(*int)(nil) = 0 | |
| return | |
| } |