Golang查缺补漏(一)

Golang查缺补漏(一),第1张

概述Go语言高级编程(Advanced Go Programming) Go语言高级编程(Advanced Go Programming) golang都是传值,与其他语言不同的是数组作为参数时,也是传值! 但是,lambda闭包引用的外部变量,则是引用! 另外,slice、string虽然也是传值,但其本质上是一个引用信息(指针、长度等信息),不涉及具体的内容。 空数组 [0]int{}、空结构体s

Go语言高级编程(Advanced Go Programming)

Go语言高级编程(Advanced Go Programming)


golang都是传值,与其他语言不同的是数组作为参数时,也是传值!
但是,lambda闭包引用的外部变量,则是引用!
另外,slice、string虽然也是传值,但其本质上是一个引用信息(指针、长度等信息),不涉及具体的内容。

空数组 [0]int{}、空结构体struct{}{} 本质上都不占用内存空间,非常好用,当然后者用的更多。

空切片 sc[:0] 在特定场合下非常有用!例如字符串的去除空格、过滤等功能。见代码

func RemoveBlank(s string)[]byte{ b := s[:0] for(i := 0; i < len(s); i++){ if s[i] != ‘ ‘{ b = append(b,s[i]) //在原内存上 *** 作,且肯定不会超出,效率非常高 } } }

切片底层是数组,如果切片一直存在,那么数组也不会被释放。所以这里可能存在严重的内存浪费行为。
例如从文件内容中查找指定的内容,则可能会发生这种情况:读取了整个文件,返回了一个很大的[]byte,但最终返回的是一个很小的[]byte,这时候底层的数组不会被释放!
这时,最好就是将获取到的结果append到全新的切片中。

func FindPhoneNumber(file string)[]byte{ b,_ := IoUtil.Readfile(file) //return regexp.MustCompile(`[0-9]+`).Find(b) //FIXME 不推荐!存在浪费的可能! b = regexp.MustCompile(`[0-9]+`).Find(b) return append([]byte{},b...) //这样就OK了 }

另外,还有一种可能,就是切片中存的是指针,当缩小切片的范围时,范围外的指针仍然存在!同样会阻碍GC的进行!

//bad demo var a[]*int{ ... } a = a[:len(a)-1] //注意:此时最后一个元素仍然存在,不会被GC!

那需要怎么做呢? 首先将不需要的元素置为nil,再切片就OK啦:

//good demo var a[]*int{ ... } a[len(a)-1] = nil // a = a[:len(a)-1]

如果切片存在的周期很短的话,可以不用刻意处理这个问题!

在main.main函数执行之前所有代码都运行在同一个goroutine,也就是程序的主系统线程中。
因此,如果某个init函数内部用go关键字启动了新的goroutine的话,新的goroutine只有在进入main.main函数之后才可能被执行到。

package main import ( "fmt" "time" ) // main.main()执行之前,只有一个main goroutine,因此,哪怕init中有goroutine,也只能等到main.main()执行时才能执行 func init() { fmt.Println("init in") go func() { fmt.Println("init goroutine in") //看看这行信息出现的时间 time.Sleep(time.Second * 5) fmt.Println("init goroutine out") }() fmt.Println("init out") } func main() { fmt.Println("-----main") time.Sleep(time.Second * 10) }

Go语言函数的递归调用深度逻辑上没有限制,函数调用的栈是不会出现溢出错误的(相对而言),因为Go语言运行时会根据需要动态地调整函数栈的大小。每个goroutine刚启动时只会分配很小的栈(4或8KB,具体依赖实现),根据需要动态调整栈的大小,栈最大可以达到GB级(依赖具体实现)。

package main import "fmt" //golang 的栈不能超过 1000000000-byte limit func main() { defer func() { if e := recover(); e != nil { fmt.Println(e) } }() n := 500000000 r := factorial(n) fmt.Printf("%v 的阶乘是:%v\n",n,r) } //factorial 计算阶乘 - 不考虑溢出 func factorial(n int) int { if n == 0 { return 1 } return n * factorial(n-1) }

因为,Go语言函数的栈不会溢出,所以普通Go程序员已经很少需要关心栈的运行机制的。
在Go语言规范中甚至故意没有讲到栈和堆的概念。
我们无法知道函数参数或局部变量到底是保存在栈中还是堆中,我们只需要知道它们能够正常工作就可以了。

因为不需要考虑堆、栈问题,所以完全可以这么写

func tmp()*int{ x := 10 return &x //这是C/CPP中是完全不允许的,因为局部变量在栈上,函数执行完毕就会被销毁 - 但是golang足够智能,会自动在堆上创建 - 如果你非要关注堆、栈的话。 }

golang足够智能,会自行判断。


未完待续

总结

以上是内存溢出为你收集整理的Golang查缺补漏(一)全部内容,希望文章能够帮你解决Golang查缺补漏(一)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1268389.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存