面试题答案
一键面试1. 避免内存泄漏的方法
- 及时释放指针引用:当指针不再需要时,将其设置为
nil
,这样垃圾回收器(GC)就可以回收相关内存。 - 合理控制指针生命周期:确保指针的生命周期与实际需求匹配,避免不必要的长生命周期指针。
2. 函数传递时的内存管理要点
- 值传递指针slice:在Go语言中,函数参数传递是值传递。当传递指针类型的slice时,实际上传递的是slice的副本,这个副本包含指向底层数组的指针、长度和容量。所以在函数内部对slice的修改(如追加元素等)可能会影响到原slice,因为它们共享底层数组。但如果在函数内部重新分配了slice,原slice不受影响。
package main
import (
"fmt"
)
type Data struct {
value int
}
func modifySlice(slice []*Data) {
newData := &Data{value: 100}
slice = append(slice, newData)
fmt.Println("In modifySlice, slice length:", len(slice))
}
func main() {
data1 := &Data{value: 1}
data2 := &Data{value: 2}
mySlice := []*Data{data1, data2}
modifySlice(mySlice)
fmt.Println("In main, slice length:", len(mySlice))
}
在上述代码中,modifySlice
函数中 append
操作会影响到 main
函数中的 mySlice
,因为它们共享底层数组。但如果在 modifySlice
中重新分配 slice
,如 slice = make([]*Data, 0)
,则 main
中的 mySlice
不受影响。
3. 循环操作时的内存管理要点
- 避免循环内不必要的指针分配:如果在循环中每次都创建新的指针并添加到slice中,可能会导致大量的内存分配和碎片化。尽量复用指针。
package main
import (
"fmt"
)
type Item struct {
id int
}
func main() {
var itemPool *Item
items := make([]*Item, 0, 10)
for i := 0; i < 10; i++ {
if itemPool == nil {
itemPool = &Item{}
}
itemPool.id = i
items = append(items, itemPool)
}
fmt.Println(items)
}
在上述代码中,通过复用 itemPool
指针,减少了在循环内的内存分配。
- 注意循环中的引用关系:确保在循环结束后,不再有不必要的指针引用,以便GC回收内存。如果在循环中创建的指针被外部变量引用,而这些指针指向的内存不再需要,要及时断开引用。例如:
package main
import "fmt"
type BigData struct {
data [1000000]int
}
func main() {
var bigPtrs []*BigData
for i := 0; i < 10; i++ {
big := &BigData{}
bigPtrs = append(bigPtrs, big)
// 如果在循环内提前释放不再需要的指针引用
if i == 5 {
bigPtrs[0] = nil
}
}
// 此时,bigPtrs[0]指向的内存可以被GC回收
fmt.Println(len(bigPtrs))
}
通过在循环内适时将不再需要的指针设置为 nil
,可以让GC及时回收相关内存。