引用开源包需要慎重

最近需要修复json,查看以前的信息,用的是github.com/RealAlexand...

这个包能力很强,大部分json都能修复。但包有个很严重的问题,某种情况下可能触发 stack overflow

go 复制代码
func main() {
	str := `
{
  "Be": "",
  "gone": "" 
}
",п"г`
	dst, err := oldrepair.RepairJSON(str)
	fmt.Println(dst, err)
}
arduino 复制代码
➜  my go run main.go
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0x140200e0390 stack=[0x140200e0000, 0x140400e0000]
fatal error: stack overflow

runtime stack:
runtime.throw({0x104d79071?, 0x100000000?})
exit status 2

后续换了新包github.com/kaptinlin/j... overflow,但是修复能力差不多弱了三倍,另外有可能panic,好在panic可以recover。

所以这引出了三个问题:

  1. 选择开源代码需要注意什么?
  2. stack overflow是怎么发生的?
  3. stack overflow发生后有什么补救措施。

选择开源代码的注意事项

我很少引用外部的开源代码,因为结果比较难以把控。印象比较深的一次是为了整数区间计算,这个比较小众,整数区间计算,我这么设计,好在是google家的,我在使用前做了大量的单元测试,上线后效果很好。

在一些相对小众的功能上,在选择 GitHub 上的开源代码时,我觉得可以从以下多个关键方面进行考虑:

项目活跃度

  • 提交频率:查看项目的提交历史,如果在近期有频繁的代码提交,说明项目处于活跃开发状态,开发者在持续对其进行改进、修复问题。比如一个热门的前端框架,每周都有几次提交,这意味着它在不断发展。
  • 问题处理情况:关注项目中 Issues(问题)板块,看新问题的创建频率以及已有问题的解决速度。如果问题能够在短时间内得到回应和处理,表明项目有活跃的维护团队。例如一些知名的开源数据库项目,能在几天内就对用户提出的问题进行解答和修复。
  • 分支合并情况:频繁的分支合并说明项目在不断整合新功能和改进。比如一个大型的开源电商系统,经常有功能分支合并到主分支,代表项目在持续迭代。

社区支持

  • 星星数量:一颗星表示用户对项目的喜爱和关注,星星数量越多,说明项目越受欢迎和受认可。像一些知名的机器学习框架,星星数量能达到十几万甚至更高。
  • Fork 数量:Fork 数量多意味着有很多人基于这个项目进行二次开发,侧面反映了项目的可扩展性和受欢迎程度。例如一些经典的开源博客框架,有大量的 Fork。
  • 参与人数:查看贡献者列表,参与人数多说明项目有一个活跃的社区,不同背景的开发者共同维护和改进项目。例如一些知名的开源操作系统,有来自全球各地的上千名开发者参与。

代码质量

  • 代码结构:良好的代码结构应该是清晰、模块化的,易于理解和维护。可以查看项目的目录结构、文件组织方式以及代码中的注释情况。比如一个遵循 MVC(模型 - 视图 - 控制器)架构的 Web 项目,代码结构层次分明。
  • 测试覆盖率:高测试覆盖率表明代码经过了充分的测试,质量更有保障。可以查看项目中的测试文件和相关测试报告。例如一些严谨的开源金融项目,测试覆盖率能达到 90% 以上。
  • 遵循的规范:查看项目是否遵循行业内的最佳实践和代码规范,这有助于保证代码的一致性和可读性。比如一个 Java 项目遵循阿里巴巴的 Java 开发手册规范。

许可证

  • 开源协议类型:不同的开源协议对使用、修改和分发代码有不同的规定。例如,MIT 协议非常宽松,允许自由使用、修改和分发代码;而 GPL 协议则要求基于该协议开源的代码所衍生的项目也必须开源。如果是商业项目使用开源代码,需要特别注意许可证是否允许商业使用。比如一个商业软件想使用某个开源库,就需要确保该开源库的许可证允许商业用途。

其它

  • 大厂背书:看看是个人开发者还是大厂推出的,一般而言大厂的质量会更好一点
  • 充足测试:在使用前,对自己要用的功能,做充足的单元测试,防止意外发生

stack overflow出现原因

Go 语言运行时会为每个 goroutine 分配一定大小的栈空间(初始栈大小一般较小,随着需求会动态增长),但当上述情况使得栈空间的使用超出了限制时,就会出现stack overflow错误 。我们可以通过debug.SetMaxStack()控制栈的大小。

在 Go 语言中,stack overflow(栈溢出)通常由以下几种情况产生:

无限递归调用

递归函数在没有正确的终止条件时,会不断地调用自身,导致栈上的函数调用信息不断增加,最终耗尽栈空间。例如:

go 复制代码
package main

func recursiveFunction() {
    recursiveFunction()
}

func main() {
    recursiveFunction()
}

在上述代码中,recursiveFunction函数没有终止条件,会一直递归调用自己,很快就会引发栈溢出错误。

深层嵌套调用

即使不是无限递归,如果函数调用的层次过深,也可能导致栈溢出。例如,一系列函数层层调用,每层调用都在栈上增加新的帧:

go 复制代码
package main

func func1() {
    func2()
}

func func2() {
    func3()
}

// 假设这里有很多类似层层调用的函数
func funcN() {
    // 没有实际操作,仅作为深层调用示例
}

func main() {
    func1()
}

如果这种嵌套调用的层次足够深,超过了 Go 运行时分配给栈的空间大小,就会发生栈溢出。

栈上数据过大

如果在函数调用过程中,函数的局部变量占用了大量的栈空间,并且同时有较多的函数调用在栈上,也可能导致栈溢出。例如:

go 复制代码
func largeStackFunction() {
	// 创建一个非常大的数组,占用大量栈空间
	bigArray := make([]int, 10000000000)
	// 这里可以有更多的操作
	fmt.Println(bigArray[0], bigArray[500])
}

func main() {
	debug.SetMaxStack(1)
	for i := 0; i < 10; i++ {
		go largeStackFunction()
	}
	time.Sleep(50 * time.Second)
}

在这个例子中,largeStackFunction函数内创建了一个非常大的数组,每次调用该函数都会在栈上占用较多空间,多次调用后可能会引发栈溢出。

stack overflow解决方案

在 Go 语言中,stack overflow(堆栈溢出)无法通过常规的recover机制捕获。这是因为堆栈溢出是一种底层的、严重的运行时错误,它会破坏调用栈的完整性,导致recover无法正常工作。

虽然无法捕获堆栈溢出错误,但可以采取一些措施来预防它:

  1. 设置递归终止条件:在递归函数中,确保有明确的终止条件,避免无限递归。
  2. 优化递归算法:对于深度递归的算法,可以考虑使用迭代或尾递归优化来减少栈空间的使用。
  3. 合理使用局部变量 :避免在函数内部声明过大的局部变量,尽量使用堆内存(通过newmake)来分配较大的数据结构。

资料

  1. Goroutine堆栈大小使用调研
  2. Golang高效编程技巧:如何设置初始栈大小优化性能与内存管理

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:shidawuhen.github.io/

往期文章回顾:

  1. 设计模式

  2. 招聘

  3. 思考

  4. 存储

  5. 算法系列

  6. 读书笔记

  7. 小工具

  8. 架构

  9. 网络

  10. Go语言

相关推荐
uzong1 小时前
技术故障复盘模版
后端
GetcharZp1 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程2 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研2 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi2 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国3 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy3 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack4 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9654 小时前
pip install 已经不再安全
后端
寻月隐君5 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github