go panic和c++ coredump差不多, 来看看go panic.
看程序:
package main
import "fmt"
func main() {
fmt.Println("main begin")
var p *int
*p = 0
fmt.Println("main end")
for {}
}
结果:
main begin
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1090f33]
goroutine 1 [running]:
main.main()
/Users/xxx/test/a.go:7 +0x63
exit status 2
可见,panic后,进程退出了。
再看:
package main
import "fmt"
import "runtime/debug"
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("haha")
debug.PrintStack()
}
}()
fmt.Println("main begin")
var p *int
*p = 0
fmt.Println("main end")
for {}
}
结果:
main begin
haha
goroutine 1 [running]:
runtime/debug.Stack(0xc00000c018, 0xc000076de8, 0x1)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22
main.main.func1()
/Users/xxx/test/a.go:9 +0x82
panic(0x10a9620, 0x115d520)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9
main.main()
/Users/xxx/test/a.go:16 +0x83
进程还是退出了,可见在main中recover并没有什么卵用。
再看:
package main
import "fmt"
import "runtime/debug"
func fun() {
defer func() {
if err := recover(); err != nil {
fmt.Println("haha")
debug.PrintStack()
}
}()
fmt.Println("fun begin")
var p *int
*p = 0
fmt.Println("fun end")
}
func main() {
fmt.Println("main begin")
fun()
fmt.Println("main end")
for {}
}
结果:
main begin
fun begin
haha
goroutine 1 [running]:
runtime/debug.Stack(0xc00008c008, 0xc000082d88, 0x1)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22
main.fun.func1()
/Users/xxx/test/a.go:9 +0x82
panic(0x10a96e0, 0x115d520)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9
main.fun()
/Users/xxx/test/a.go:15 +0x83
main.main()
/Users/xxx/test/a.go:22 +0x66
main end
可见,在fun层panic后退出了,没有被for{}卡住,但在main层没有退出,被for{}卡住了, 进程还是活着的。
那好,现在来看看:
package main
import "fmt"
import "runtime/debug"
func high() {
fmt.Println("high begin")
middle()
fmt.Println("high end")
}
func middle() {
fmt.Println("middle begin")
low()
var p *int
*p = 0
fmt.Println("middle end")
}
func low() {
fmt.Println("low begin")
fmt.Println("low end")
}
func fun() {
fmt.Println("fun begin")
defer func() {
debug.PrintStack()
}()
fmt.Println("yyy")
high()
fmt.Println("zzz")
fmt.Println("fun end")
for {}
}
func main() {
fmt.Println("main begin")
fun()
a := 2
b := 3
fmt.Println("main end", a * b)
for {}
}
结果:
main begin
fun begin
yyy
high begin
middle begin
low begin
low end
goroutine 1 [running]:
runtime/debug.Stack(0x0, 0x10, 0x8)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22
main.fun.func1()
/Users/xxx/test/a.go:28 +0x20
panic(0x10a9a00, 0x115e520)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9
main.middle()
/Users/xxx/test/a.go:15 +0x68
main.high()
/Users/xxx/test/a.go:7 +0x66
main.fun()
/Users/xxx/test/a.go:32 +0xc6
main.main()
/Users/xxx/test/a.go:42 +0x66
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1091528]
goroutine 1 [running]:
main.middle()
/Users/xxx/test/a.go:15 +0x68
main.high()
/Users/xxx/test/a.go:7 +0x66
main.fun()
/Users/xxx/test/a.go:32 +0xc6
main.main()
/Users/xxx/test/a.go:42 +0x66
exit status 2
很显然,没有recover去处理panic信息,所以整个进程崩溃, 如下:
再来看看:
package main
import "fmt"
import "runtime/debug"
func high() {
fmt.Println("high begin")
middle()
fmt.Println("high end")
}
func middle() {
fmt.Println("middle begin")
low()
var p *int
*p = 0
fmt.Println("middle end")
}
func low() {
fmt.Println("low begin")
fmt.Println("low end")
}
func fun() {
fmt.Println("fun begin")
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
}
}()
fmt.Println("yyy")
high()
fmt.Println("zzz")
fmt.Println("fun end")
for {}
}
func main() {
fmt.Println("main begin")
fun()
a := 2
b := 3
fmt.Println("main end", a * b)
for {}
}
结果:
main begin
fun begin
yyy
high begin
middle begin
low begin
low end
goroutine 1 [running]:
runtime/debug.Stack(0x115e5c0, 0xc000042c00, 0x0)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22
main.fun.func1()
/Users/xxx/test/a.go:29 +0x42
panic(0x10a9a20, 0x115e520)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9
main.middle()
/Users/xxx/test/a.go:15 +0x68
main.high()
/Users/xxx/test/a.go:7 +0x66
main.fun()
/Users/xxx/test/a.go:34 +0xc6
main.main()
/Users/xxx/test/a.go:44 +0x66
main end 6
可以看到middle层有panic, 故提前异常退出middle, high和fun, 但在fun层被recover了,所以main层仍然正常,整个进程不会崩溃,仍然活得好好的。
最后看看,如何获取堆栈信息的串呢? 且看:
package main
import "fmt"
import "runtime/debug"
func fun() {
fmt.Println("fun begin")
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
fmt.Println("xxx", string(debug.Stack()))
}
}()
var p *int
*p = 0
fmt.Println("fun end")
for {}
}
func main() {
fmt.Println("main begin")
fun()
fmt.Println("main end")
for {}
}
结果:
main begin
fun begin
goroutine 1 [running]:
runtime/debug.Stack(0xc000088060, 0xc00009a000, 0xa)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22
main.fun.func1()
/Users/xxx/test/a.go:10 +0x46
panic(0x10a9760, 0x115d520)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9
main.fun()
/Users/xxx/test/a.go:16 +0x7f
main.main()
/Users/xxx/test/a.go:24 +0x66
xxx goroutine 1 [running]:
runtime/debug.Stack(0xc00007ada8, 0x10a9760, 0x115d520)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7
main.fun.func1()
/Users/xxx/test/a.go:11 +0x4b
panic(0x10a9760, 0x115d520)
/usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9
main.fun()
/Users/xxx/test/a.go:16 +0x7f
main.main()
/Users/xxx/test/a.go:24 +0x66
main end
总之,panic触发层到recover层都会异常退出。 但recover层之上不会异常退出。
不多说。