记升级至golang 1.18以上项目无法启动

前言

在升级至新版goland后,debug旧版golang编译的可执行文件有点问题。于是就打算升级一下本地golang的版本。在升级到golang 1.18以上后,项目启动过程中就奔溃了。也没有具体的报错行数,倒是把所有的协程堆栈都打印出来了,实在难以排查。
崩溃的部分控制台输出

1
2
unexpected fault address 0xffffffffffffffff
fatal error: fault

探索

经过实验,装了n多个版本,1.18到1.22全都是这样,一旦降级到1.17就可以正常启动。看着1.18的release notes说As always, the release maintains the Go 1 promise of compatibility. We expect almost all Go programs to continue to compile and run as before.不禁陷入沉思。
又进行了一波排查,似乎是在打日志的时候崩溃的。项目为了优化序列化与反序列化的性能用了json-iterator,怀疑可能是json-iterator的骚操作导致的崩溃。
于是去github上看了一下,果然看到了相关的issue https://github.com/json-iterator/go/issues/608。
看了一下相关的提交后,所谓的修复方式是将reflect2升级至v1.0.2这个版本。
json-iterator使用了reflect2这个包对map进行了一些unsafe的操作,导致了整个程序崩溃。
在golang1.18以前reflect/value.go中的某个函数定义是这样的

1
func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer

在golang 1.18及以后变成了

1
func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter)

新版本的reflect2针对不同的golang版本使用了//+build指令,针对不同版本的golang进行条件编译。

小结

在我们的项目中string跟[]byte的一些转换用的也是unsafe的操作,能够减少内存拷贝啥的,以提高性能。
unsafe 包提供了一些低级操作,如指针算术、类型转换等,可以绕过 Go 语言的类型系统和内存安全机制。由于它使得代码绕过了编译器的一些安全检查,可能导致程序运行时的崩溃或者不可预料的行为,所以使用unsafe需谨慎,毕竟他就叫unsafe。

作者

ZhongHuihong

发布于

2024-03-14

更新于

2024-04-10

许可协议