golang WaitGroup 中用到的内存对齐方式会受到 gc 的影响吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
BBCCBB

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

  •  1
     
  •   BBCCBB 2021 年 9 月 27 日 2374 次点击
    这是一个创建于 1670 天前的主题,其中的信息可能已经有所发展或是发生改变。

    RT

    go WaitGroup 中有这样的代码:

    type WaitGroup struct { noCopy noCoy // 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

    17 条回复    2021-09-30 10:26:08 +08:00
    BBCCBB
        1
    BBCCBB  
    OP
       2021 年 9 月 27 日
    ```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
        2
    BBCCBB  
    OP
       2021 年 9 月 27 日
    64 位对齐不是说数据的地址是 8 的整数倍吗... 那为啥这里还有 0 和 4 两种结果?
    katsusan
        3
    katsusan  
       2021 年 9 月 27 日
    @BBCCBB #1 加一个 fmt.Println(wg)使其在堆上分配
    BBCCBB
        4
    BBCCBB  
    OP
       2021 年 9 月 27 日
    @katsusan 这是个啥神奇的操作, 怪异但有效.... 离谱
    Huelse
        5
    Huelse  
       2021 年 9 月 28 日
    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
        6
    katsusan  
       2021 年 9 月 28 日
    > 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)
    }
    ```
    6ufq0VLZn0DDkL80
        7
    6ufq0VLZn0DDkL80  
       2021 年 9 月 28 日
    "gc 后地址就变了" 是啥意思? go 没有移动 gc
    BBCCBB
        8
    BBCCBB  
    OP
       2021 年 9 月 28 日
    @cholerae 就是 gc 后一个 waitGroup 对象的地址就变了.
    在 jvm 里, gc 是会移动内存的.
    BBCCBB
        9
    BBCCBB  
    OP
       2021 年 9 月 28 日
    @katsusan
    > 64 位对齐不是说数据的地址是 8 的整数倍吗... 那为啥这里还有 0 和 4 两种结果?
    这个看下来只要地址是 unsafe.AlignOf(wg)的 整数倍就行了? 64 位机器上没要求对象地址必须是 8 的整数倍?


    gc 对象位置在内存里被移动 这个问题大佬知道吗?
    katsusan
        10
    katsusan  
       2021 年 9 月 28 日
    @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
        11
    BBCCBB  
    OP
       2021 年 9 月 28 日
    @katsusan jvm 老年代里的 gc 有 mark-sweep, 也有 mark-sweep & compact, 压缩, 防止内存碎片用的,

    go 里只有标记清除, 没有整理内存, 压缩这个操作是不?
    BBCCBB
        12
    BBCCBB  
    OP
       2021 年 9 月 28 日
    @katsusan 对的, 我说的就是压缩内存, 防止内存碎片过程中移动内存这个操作,
    BBCCBB
        13
    BBCCBB  
    OP
       2021 年 9 月 28 日
    @katsusan 简单看了下 go 的 gc, 没有 compact 操作, 内存都被 tmolloc 这种分配器切成一块一块的..
    BBCCBB
        14
    BBCCBB  
    OP
       2021 年 9 月 28 日
    @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
        15
    BBCCBB  
    OP
       2021 年 9 月 29 日
    地址按照这个规则来对齐

    uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
    my3157
        16
    my3157  
       2021 年 9 月 29 日 via Android
    @BBCCBB #4
    > 这是个啥神奇的操作, 怪异但有效.... 离谱

    加了 println 逃逸了
    go build -gcflags '-m -m -l' main.go
    BBCCBB
        17
    BBCCBB  
    OP
       2021 年 9 月 30 日
    @my3157 蟹蟹, 我已经悟了,

    type Wg struct {

    state1 [3]uint32
    }

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

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


    所以出现 0/4 是对的.
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1065 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 68ms UTC 23:17 PVG 07:17 LAX 16:17 JFK 19:17
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86