不要以为Go的所有异常都能Recover住!

45 阅读1分钟

最近部门线上有一起panic导致的事故,基本都是犯的编程中的一些低级错误;引以为戒,总结了下Go里面常见的一些异常捕获以及哪一些异常不能捕获

能Recover的异常

下面几种情况是能 Recover 住的

切片或者数组下标越界

func main() {
    go func() {
        if err := recover(); err != nil {
            fmt.Println("err:", err)
        }
    }()
    nums := []int{}
    fmt.Println(nums[1])
}

空指针异常

func main() {
    var str *string
    fmt.Println(*str)
}

未初始化的map进行写

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("err:", err)
        }
    }()
    var mp map[string]int
    mp[""] = 1
}

重复关闭channel或者channle已关闭还继续写入数据

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("err:", err)
        }
    }()
    stopCh := make(chan struct{})
    close(stopCh)

    close(stopCh)        // 这里会panic
    
    stopCh <- struct{}{} // 这里也会panic
}

类型断言

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("panic")
        }
    }()

    var str interface{} = "123"
    _ = str.(int)  // 不要使用这种类型断言
}

不能Recover住的异常

下面几种情况是不能 Recover 住的

内存溢出

func main() {
    _ = make([]int64, 1<<40)
    fmt.Println("cover")
}

Map 并发读写

func main() {
    m := map[string]int{}

    go func() {
        for {
            m["x"] = 1
        }
    }()
    for {
        _ = m["y"]
    }
}

栈内存耗尽

func main() {
    var fun func([1000]int64)
    fun = func(i [1000]int64) {
        fun(i)
    }
    fun([1000]int64{})
}

报错 runtime: goroutine stack exceeds 1000000000-byte limit

将 Nil 函数交给 goroutine 启动

func main() {
    var f func()
    go f()
}

报错 fatal error: go of nil func value

程序死锁

func main() {
    go func() {
        for true {
            fmt.Println("alive")
            time.Sleep(time.Second * 1)
            select {}
        }
    }()
    <-make(chan int)
}