golang WaitGroup 中用到的内存对齐方式会受到 gc 的影响吗?

2021-09-27 22:21:56 +08:00
 BBCCBB

RT

go WaitGroup 中有这样的代码:

type WaitGroup struct {
	noCopy noCopy

	// 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
	// 64-bit atomic operations require 64-bit alignment, but 32-bit
	// compilers do not ensure it. So we allocate 12 bytes and then use
	// the aligned 8 bytes in them as state, and the other 4 as storage
	// for the sema.
	state1 [3]uint32
}


func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {  
 if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {  
  return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2]  
 } else {  
  return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0]  
 }  
}  

这个 uintptr(unsafe.Pointer(&wg.state1))会不会受到 gc 的影响, 比如在 gc 后地址就变了..

变成%8==4 了.. (包括在 32 位对齐的机器上是否有地址改变这种问题) ??

有知道的大佬多说说 🐶

相关知识链接(随便找个): https://www.helloworld.net/p/8378277893

1987 次点击
所在节点    程序员
17 条回复
BBCCBB
2021-09-27 22:25:06 +08:00
```go
type Wg struct {

state1 [3]uint32
}

func main() {
var wg Wg
fmt.Println(uintptr(unsafe.Pointer(&wg.state1)) % 8)
}
```

64 位 mac 上执行这个代码多次, 是会出现 0 和 4 两种结果的
BBCCBB
2021-09-27 22:54:01 +08:00
64 位对齐不是说数据的地址是 8 的整数倍吗... 那为啥这里还有 0 和 4 两种结果?
katsusan
2021-09-27 23:44:00 +08:00
@BBCCBB #1 加一个 fmt.Println(wg)使其在堆上分配
BBCCBB
2021-09-27 23:54:00 +08:00
@katsusan 这是个啥神奇的操作, 怪异但有效.... 离谱
Huelse
2021-09-28 10:21:07 +08:00
the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.

https://pkg.go.dev/unsafe#Pointer
katsusan
2021-09-28 11:34:56 +08:00
> 64 位对齐不是说数据的地址是 8 的整数倍吗... 那为啥这里还有 0 和 4 两种结果?

1 楼例子里的 Wg 是 4 字节对齐的,unsafe.Alignof(wg)或 reflect.TypeOf(wg).Align()可以看到。

> 64 位 mac 上执行这个代码多次, 是会出现 0 和 4 两种结果的

wg 分配在栈上,受 runtime 申请栈空间时获得的地址影响,不一定保证 8 字节对齐还是只满足 4 字节对齐。

> 加一个 fmt.Println(wg)使其在堆上分配

加 fmt.Println 让 wg 分配在堆上利用了 tiny allocator 获得的 tiny block 地址在 64 位环境下为 8 字节对齐,
不代表内存分配器一定会给 wg 返回 8 字节对齐的地址,比如下面的代码里 wg 的地址应该只满足 4 字节对齐。

```Go
type Wg struct {

state1 [3]uint32
}

func main() {
var b bool
var wg Wg
fmt.Printf("%p\n", &b)
fmt.Printf("%p\n", &wg)
}
```
cholerae
2021-09-28 13:23:01 +08:00
"gc 后地址就变了" 是啥意思? go 没有移动 gc
BBCCBB
2021-09-28 13:49:00 +08:00
@cholerae 就是 gc 后一个 waitGroup 对象的地址就变了.
在 jvm 里, gc 是会移动内存的.
BBCCBB
2021-09-28 13:52:20 +08:00
@katsusan
> 64 位对齐不是说数据的地址是 8 的整数倍吗... 那为啥这里还有 0 和 4 两种结果?
这个看下来只要地址是 unsafe.AlignOf(wg)的 整数倍就行了? 64 位机器上没要求对象地址必须是 8 的整数倍?


gc 对象位置在内存里被移动 这个问题大佬知道吗?
katsusan
2021-09-28 16:17:31 +08:00
@BBCCBB #9
> 64 位机器上没要求对象地址必须是 8 的整数倍?
除了一些特别的操作比如 atomic 外,没有强制。但地址对齐对访问内存性能有影响,非对齐地址会导致需要拆分成两次内存访问。在 amd64 下,对于 byte 型数据需要 1 字节对齐,word 需要 2 字节,doubleword 需要 4 字节,quadword 需要 8 字节对齐。

> gc 对象位置在内存里被移动
我看了下 JAVA 的各式 GC 算法里普遍对 young generation 使用 mark-copy 算法,对 old generation 使用 mark-sweep 算法。
你说的移动指的是 copy 过程中的移动吧,Golang 里未采用分代 GC,只有 mark-sweep 。
BBCCBB
2021-09-28 17:06:01 +08:00
@katsusan jvm 老年代里的 gc 有 mark-sweep, 也有 mark-sweep & compact, 压缩, 防止内存碎片用的,

go 里只有标记清除, 没有整理内存, 压缩这个操作是不?
BBCCBB
2021-09-28 17:07:01 +08:00
@katsusan 对的, 我说的就是压缩内存, 防止内存碎片过程中移动内存这个操作,
BBCCBB
2021-09-28 17:13:02 +08:00
@katsusan 简单看了下 go 的 gc, 没有 compact 操作, 内存都被 tmolloc 这种分配器切成一块一块的..
BBCCBB
2021-09-28 17:48:25 +08:00
@katsusan 我还看到一个概念


The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.

变量或开辟的结构体、数组和切片值中的第一个 64 位字可以被认为是 8 字节对齐
这一句中开辟的意思是通过声明,make,new 方式创建的,就是说这样创建的 64 位字可以保证是 64 位对齐的。
BBCCBB
2021-09-29 00:00:08 +08:00
地址按照这个规则来对齐

uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
my3157
2021-09-29 04:58:35 +08:00
@BBCCBB #4
> 这是个啥神奇的操作, 怪异但有效.... 离谱

加了 println 逃逸了
go build -gcflags '-m -m -l' main.go
BBCCBB
2021-09-30 10:26:08 +08:00
@my3157 蟹蟹, 我已经悟了,

type Wg struct {

state1 [3]uint32
}

这个本来就是 32 位对齐的, 因为按下面这个规则来对齐的,

uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0


所以出现 0/4 是对的.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/804736

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX