golang的append原理分析

mediaufo · · 1372 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

# golang的append原理分析 append是编译器build处理的。 一、看下面的一个简单代码 ``` func testappend() { var buf []byte buf = append(buf, byte(1)) } func main() { testappend() } ``` 对上面的代码golang反汇编 编译指令 go build -gcflags "-S" testbuffer.go ``` "".testappend STEXT size=85 args=0x0 locals=0x20 0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) TEXT "".testappend(SB), $32-0 0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL TLS, CX 0x0007 00007 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL (CX)(TLS*2), CX 0x000d 00013 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CMPL SP, 8(CX) 0x0010 00016 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JLS 78 0x0012 00018 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) SUBL $32, SP 0x0015 00021 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0015 00021 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0015 00021 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) LEAL type.uint8(SB), AX 0x001b 00027 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL AX, (SP) 0x001e 00030 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 4(SP) 0x0026 00038 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 8(SP) 0x002e 00046 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 12(SP) 0x0036 00054 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $1, 16(SP) 0x003e 00062 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) PCDATA $0, $0 0x003e 00062 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) CALL runtime.growslice(SB) 0x0043 00067 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 20(SP), AX 0x0047 00071 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVB $1, (AX) 0x004a 00074 (E:\work\workspace\loggoper\src\test\testbuffer.go:29) ADDL $32, SP 0x004d 00077 (E:\work\workspace\loggoper\src\test\testbuffer.go:29) RET 0x004e 00078 (E:\work\workspace\loggoper\src\test\testbuffer.go:29) NOP 0x004e 00078 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) PCDATA $0, $-1 0x004e 00078 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CALL runtime.morestack_noctxt(SB) 0x0053 00083 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JMP 0 ``` 所以 append调用的是growslice 关于growslice: go/src/runtime/slice.go ``` // growslice handles slice growth during append. // It is passed the slice element type, the old slice, and the desired new minimum capacity, // and it returns a new slice with at least that capacity, with the old data // copied into it. // The new slice's length is set to the old slice's length, // NOT to the new requested capacity. // This is for codegen convenience. The old slice's length is used immediately // to calculate where to write new values during an append. // TODO: When the old backend is gone, reconsider this decision. // The SSA backend might prefer the new length or to return only ptr/cap and save stack space. type slice struct { array unsafe.Pointer len int cap int } func growslice(et *_type, old slice, cap int) slice { if raceenabled { callerpc := getcallerpc(unsafe.Pointer(&et)) racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice)) } if msanenabled { msanread(old.array, uintptr(old.len*int(et.size))) } if et.size == 0 { if cap < old.cap { panic(errorString("growslice: cap out of range")) } // append should not create a slice with nil pointer but non-zero len. // We assume that append doesn't need to preserve old.array in this case. return slice{unsafe.Pointer(&zerobase), old.len, cap} } newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap } else { if old.len < 1024 { newcap = doublecap } else { for newcap < cap { newcap += newcap / 4 } } } var lenmem, newlenmem, capmem uintptr const ptrSize = unsafe.Sizeof((*byte)(nil)) switch et.size { case 1: lenmem = uintptr(old.len) newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) newcap = int(capmem) case ptrSize: lenmem = uintptr(old.len) * ptrSize newlenmem = uintptr(cap) * ptrSize capmem = roundupsize(uintptr(newcap) * ptrSize) newcap = int(capmem / ptrSize) default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size capmem = roundupsize(uintptr(newcap) * et.size) newcap = int(capmem / et.size) } if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) { panic(errorString("growslice: cap out of range")) } var p unsafe.Pointer if et.kind&kindNoPointers != 0 { p = mallocgc(capmem, nil, false) memmove(p, old.array, lenmem) // The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length). // Only clear the part that will not be overwritten. memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. p = mallocgc(capmem, et, true) if !writeBarrier.enabled { memmove(p, old.array, lenmem) } else { for i := uintptr(0); i < lenmem; i += et.size { typedmemmove(et, add(p, i), add(old.array, i)) } } } return slice{p, old.len, newcap} } ``` 上面的代码不难看出分配的空间是按照2倍的容量开辟的. 我们把上面的测试代码稍微改一下,改成如下格式: ``` func testappend() { var buf []byte buf = append(buf, byte(1)) buf = append(buf, byte(2)) buf = buf[:0] buf = append(buf, byte(3)) } ``` 最后的一行buf = append(buf, byte(3)) 会在重新分配吗? ``` "".testappend STEXT size=215 args=0x0 locals=0x24 0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) TEXT "".testappend(SB), $36-0 0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL TLS, CX 0x0007 00007 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL (CX)(TLS*2), CX 0x000d 00013 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CMPL SP, 8(CX) 0x0010 00016 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JLS 205 0x0016 00022 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) SUBL $36, SP 0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) LEAL type.uint8(SB), AX 0x001f 00031 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL AX, (SP) 0x0022 00034 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 4(SP) 0x002a 00042 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 8(SP) 0x0032 00050 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 12(SP) 0x003a 00058 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $1, 16(SP) 0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) PCDATA $0, $0 0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) CALL runtime.growslice(SB) 0x0047 00071 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 20(SP), AX 0x004b 00075 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 24(SP), CX 0x004f 00079 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 28(SP), DX 0x0053 00083 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVB $1, (AX) 0x0056 00086 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) LEAL 1(CX), BX 0x0059 00089 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) LEAL 2(CX), BP 0x005c 00092 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CMPL BP, DX 0x005e 00094 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JGT 157 0x0060 00096 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVB $2, 1(CX)(AX*1) 0x0065 00101 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CMPL DX, $1 0x0068 00104 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JLT 113 0x006a 00106 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVB $3, (AX) 0x006d 00109 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) ADDL $36, SP 0x0070 00112 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) RET 0x0071 00113 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) LEAL type.uint8(SB), CX 0x0077 00119 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, (SP) 0x007a 00122 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL AX, 4(SP) 0x007e 00126 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL $0, 8(SP) 0x0086 00134 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL DX, 12(SP) 0x008a 00138 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL $1, 16(SP) 0x0092 00146 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) PCDATA $0, $0 0x0092 00146 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CALL runtime.growslice(SB) 0x0097 00151 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL 20(SP), AX 0x009b 00155 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JMP 106 0x009d 00157 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, ""..autotmp_4+32(SP) 0x00a1 00161 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) LEAL type.uint8(SB), CX 0x00a7 00167 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL CX, (SP) 0x00aa 00170 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL AX, 4(SP) 0x00ae 00174 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BX, 8(SP) 0x00b2 00178 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL DX, 12(SP) 0x00b6 00182 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BP, 16(SP) 0x00ba 00186 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) PCDATA $0, $0 0x00ba 00186 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CALL runtime.growslice(SB) 0x00bf 00191 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 20(SP), AX 0x00c3 00195 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 28(SP), DX 0x00c7 00199 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL ""..autotmp_4+32(SP), CX 0x00cb 00203 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JMP 96 0x00cd 00205 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) NOP 0x00cd 00205 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) PCDATA $0, $-1 0x00cd 00205 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CALL runtime.morestack_noctxt(SB) 0x00d2 00210 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JMP 0 ``` ![image-20210221160812103](C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210221160812103.png) 如果不执行30行的代码 再反编译看看。 ``` "".testappend STEXT size=225 args=0x0 locals=0x28 0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) TEXT "".testappend(SB), $40-0 0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL TLS, CX 0x0007 00007 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL (CX)(TLS*2), CX 0x000d 00013 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CMPL SP, 8(CX) 0x0010 00016 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JLS 215 0x0016 00022 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) SUBL $40, SP 0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) LEAL type.uint8(SB), AX 0x001f 00031 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL AX, (SP) 0x0022 00034 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 4(SP) 0x002a 00042 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 8(SP) 0x0032 00050 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 12(SP) 0x003a 00058 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $1, 16(SP) 0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) PCDATA $0, $0 0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) CALL runtime.growslice(SB) 0x0047 00071 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 20(SP), AX 0x004b 00075 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 24(SP), CX 0x004f 00079 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 28(SP), DX 0x0053 00083 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVB $1, (AX) 0x0056 00086 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) LEAL 1(CX), BX 0x0059 00089 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) LEAL 2(CX), BP 0x005c 00092 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CMPL BP, DX 0x005e 00094 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JGT 160 0x0060 00096 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVB $2, 1(CX)(AX*1) 0x0065 00101 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) LEAL 1(BP), CX 0x0068 00104 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CMPL CX, DX 0x006a 00106 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JGT 116 0x006c 00108 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVB $3, (AX)(BP*1) 0x0070 00112 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) ADDL $40, SP 0x0073 00115 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) RET 0x0074 00116 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) MOVL BP, "".buf.len+32(SP) 0x0078 00120 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) LEAL type.uint8(SB), BX 0x007e 00126 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL BX, (SP) 0x0081 00129 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL AX, 4(SP) 0x0085 00133 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL BP, 8(SP) 0x0089 00137 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL DX, 12(SP) 0x008d 00141 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, 16(SP) 0x0091 00145 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) PCDATA $0, $0 0x0091 00145 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CALL runtime.growslice(SB) 0x0096 00150 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL 20(SP), AX 0x009a 00154 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL "".buf.len+32(SP), BP 0x009e 00158 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JMP 108 0x00a0 00160 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, ""..autotmp_4+36(SP) 0x00a4 00164 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) LEAL type.uint8(SB), CX 0x00aa 00170 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL CX, (SP) 0x00ad 00173 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL AX, 4(SP) 0x00b1 00177 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BX, 8(SP) 0x00b5 00181 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL DX, 12(SP) 0x00b9 00185 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BP, 16(SP) 0x00bd 00189 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) PCDATA $0, $0 0x00bd 00189 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CALL runtime.growslice(SB) 0x00c2 00194 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 20(SP), AX 0x00c6 00198 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 24(SP), CX 0x00ca 00202 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 28(SP), DX 0x00ce 00206 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) LEAL 1(CX), BP 0x00d1 00209 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL ""..autotmp_4+36(SP), CX 0x00d5 00213 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JMP 96 0x00d7 00215 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) NOP 0x00d7 00215 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) PCDATA $0, $-1 0x00d7 00215 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CALL runtime.morestack_noctxt(SB) 0x00dc 00220 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JMP 0 ``` 总结:通过汇编代码不难看出 ,即使len为0了,还是会从原来的分配中赋值。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1372 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传