-
Notifications
You must be signed in to change notification settings - Fork 0
20210127从切片的组成方式思考数据销毁的原理
在一个作用域中:
func test(){
//都是作用域
//都是作用域
//都是作用域
//都是作用域
}
或
for{
//都是作用域
//都是作用域
//都是作用域
//都是作用域
}
进入正题:
当你完成初始化一个切片时,内存中会存在两个东西:
一个是这切片
另一个是这切片的底层数组,底层数组没有变量名
这个切片内部有一个引用(指针)字段,其值指向了底层数组
当他所在的作用域接收后,销毁逻辑是:
由于没有别的地方有引用指向这个切片的地址(注意是切片的地址,而不是其内部的指针容器字段),切片先被销毁
由于切片销毁了,于是也就没有别的引用指向底层数组了,同理底层数组被销毁
这里有个先后顺序:无论什么类型,先创建的先销毁,于是销毁切片在先,销毁底层数组在后
咱们再来探讨如下操作会发生什么:
for{
test1 := []int{1,2,3}
fmt.Println("test1:",test1)
fmt.Printf("test1 ptr:%p\n",test1)
//test1 =nil
//fmt.Println("test1:",test1)
//fmt.Printf("test1 ptr:%p\n",test1)
}
没有后三句的话就是标准的销毁流程,如果加上后三句,赋nil后相当于主动切断了切片与底层数组间的联系,此时底层熟读相当于c++中“野指针”的状态
销毁的时候,其实也差不多,这种情况其实没啥可深入聊的
test := []int{1,2,3}
fmt.Println("test:",test)
fmt.Printf("test ptr:%p\n",test)
for{
test1 := test
fmt.Println("test1:",test1)
fmt.Printf("test1 ptr:%p\n",test1)
//test1 =nil
//fmt.Println("test:",test)
//fmt.Printf("test ptr:%p\n",test)
}
select{}
这种方式也并不会影响test的值,test1 =nil的操作只会修改test1自身的指向方向,不会对test的指向方向产生影响,也不会改变底层字段的值
但是如果进行test1 =append(test1,4,5)或test1[0]=100就完全不同了,会直接修改底层的值
同时还要明确的是,append函数没有像copy那样设计成深拷贝的特性
另一方面,每次for结束只会销毁test1,test依然会指向底层数组,底层数组会安然无恙,但是值是否变化就看你的具体操作了
尤其是,test1 =nil这句并不会导致for循环结束同时销毁底层,因为外层始终有引用指向这个“野指针”,除非发生类似如下的事:
for{
test1 := []int{1,2,3}
fmt.Println("test1:",test1)
fmt.Printf("test1 ptr:%p\n",test1)
//test1 =nil
//fmt.Println("test1:",test1)
//fmt.Printf("test1 ptr:%p\n",test1)
}
没有后三句的话就是标准的销毁流程,如果加上后三句,赋nil后相当于主动切断了切片与底层数组间的联系,此时底层熟读相当于c++中“野指针”的状态
销毁的时候,其实也差不多,这种情况其实没啥可深入聊的
test := []int{1,2,3}
fmt.Println("test:",test)
fmt.Printf("test ptr:%p\n",test)
i :=0
for{
test1 := test
fmt.Println("test1:",test1)
fmt.Printf("test1 ptr:%p\n",test1)
i++
if (i ==8){
test =nil
}
}
select{}
当运行到i等于8的这次循环,循环结束后,test1会和前7次一样被销毁,而底层数组也会被这次作用域结束事件被销毁,因为再没有任何指向他的引用了
虽然外层的test依然存在,但是底层数组已经不存在了