This commit is contained in:
Xargin
2016-10-10 14:53:50 +08:00
parent 7d40fb4d28
commit ef1d302ae6
6 changed files with 12 additions and 12 deletions

View File

@@ -42,7 +42,7 @@ pb := (*int16)(unsafe.Pointer(tmp))
*pb = 42
```
产生错误的原因很微妙。有时候垃圾回收器会移动一些变量以降低内存碎片等问题。这类垃圾回收器被称为移动GC。当一个变量被移动所有的保存变量旧地址的指针必须同时被更新为变量移动后的新地址。从垃圾收集器的视角来看一个unsafe.Pointer是一个指向变量的指针因此当变量被移动对应的指针也必须被更新但是uintptr类型的临时变量只是一个普通的数字所以其值不应该被改变。上面错误的代码因为引入一个非指针的临时变量tmp导致垃圾收集器无法正确识别这个是一个指向变量x的指针。当第二个语句执行时变量x可能已经被转移这时候临时变量tmp也就不再是现在的`&x.b`地址。第三个向之前无效地址空间的赋值语句将彻底摧毁整个程序!
产生错误的原因很微妙。有时候垃圾回收器会移动一些变量以降低内存碎片等问题。这类垃圾回收器被称为移动GC。当一个变量被移动所有的保存变量旧地址的指针必须同时被更新为变量移动后的新地址。从垃圾收集器的视角来看一个unsafe.Pointer是一个指向变量的指针因此当变量被移动对应的指针也必须被更新但是uintptr类型的临时变量只是一个普通的数字所以其值不应该被改变。上面错误的代码因为引入一个非指针的临时变量tmp导致垃圾收集器无法正确识别这个是一个指向变量x的指针。当第二个语句执行时变量x可能已经被转移这时候临时变量tmp也就不再是现在的`&x.b`地址。第三个向之前无效地址空间的赋值语句将彻底摧毁整个程序!
还有很多类似原因导致的错误。例如这条语句:
@@ -52,7 +52,7 @@ pT := uintptr(unsafe.Pointer(new(T))) // 提示: 错误!
这里并没有指针引用`new`新创建的变量因此该语句执行完成之后垃圾收集器有权马上回收其内存空间所以返回的pT将是无效的地址。
虽然目前的Go语言实现还没有使用移动GC译注未来可能实现但这不该是编写错误代码侥幸的理由当前的Go语言实现已经有移动变量的场景。在5.2节我们提到goroutine的栈是根据需要动态增长的。当发栈动态增长的时候,原来栈中的所变量可能需要被移动到新的更大的栈中,所以我们并不能确保变量的地址在整个使用周期内是不变的。
虽然目前的Go语言实现还没有使用移动GC译注未来可能实现但这不该是编写错误代码侥幸的理由当前的Go语言实现已经有移动变量的场景。在5.2节我们提到goroutine的栈是根据需要动态增长的。当发栈动态增长的时候,原来栈中的所变量可能需要被移动到新的更大的栈中,所以我们并不能确保变量的地址在整个使用周期内是不变的。
在编写本文时还没有清晰的原则来指引Go程序员什么样的unsafe.Pointer和uintptr的转换是不安全的参考 [Issue7192](https://github.com/golang/go/issues/7192) . 译注: 该问题已经关闭因此我们强烈建议按照最坏的方式处理。将所有包含变量地址的uintptr类型变量当作BUG处理同时减少不必要的unsafe.Pointer类型到uintptr类型的转换。在第一个例子中有三个转换——字段偏移量到uintptr的转换和转回unsafe.Pointer类型的操作——所有的转换全在一个表达式完成。