Skip to content

20210127从切片的组成方式思考数据销毁的原理

ziyouzy edited this page Jan 27, 2021 · 1 revision

在一个作用域中:

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依然存在,但是底层数组已经不存在了

Clone this wiki locally