slice扩容
扩容的源码
func growslice(et *_type, old slice, cap int) slice {
...
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap
}
}
}
...
switch {
case et.size == 1:
lenmem = uintptr(old.len)
newlenmem = uintptr(cap)
capmem = roundupsize(uintptr(newcap))
overflow = uintptr(newcap) > maxAlloc
newcap = int(capmem)
case et.size == sys.PtrSize:
lenmem = uintptr(old.len) * sys.PtrSize
newlenmem = uintptr(cap) * sys.PtrSize
capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
newcap = int(capmem / sys.PtrSize)
case isPowerOfTwo(et.size):
var shift uintptr
if sys.PtrSize == 8 {
// Mask shift for better code generation.
shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
} else {
shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
}
lenmem = uintptr(old.len) << shift
newlenmem = uintptr(cap) << shift
capmem = roundupsize(uintptr(newcap) << shift)
overflow = uintptr(newcap) > (maxAlloc >> shift)
newcap = int(capmem >> shift)
default:
lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
capmem = roundupsize(capmem)
newcap = int(capmem / et.size)
}
}
package main import (
"fmt"
"unsafe"
) type ss struct {
ptr unsafe.Pointer
l int
c int
} type A struct {
f bool
b int32
a int
c int64
d string
} func main() {
Af()
return
} func Af() {
a := make([]A, , )
b := A{} fmt.Println("Sizeof A:", unsafe.Sizeof(b)) p1 := (*ss)(unsafe.Pointer(&a))
fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1)
a = append(a, A{}, A{}, A{}, A{})
fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1) for i := ; i < ; i++ {
a = append(a, A{})
fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1)
} }
部分输出
Sizeof A: 40
len=1,cap=1,ptr=0xc00005c420
len=5,cap=5,ptr=0xc00005c420
len=6,cap=10,ptr=0xc00005c420
。。。
len=1638,cap=1638,ptr=0xc00005c420
len=1639,cap=2048,ptr=0xc00005c420
。。。
扩容分析
1、a = append(a, A{}, A{}, A{}, A{})
增加4个元素
原容量是1,元素个数增加了4个,不够存储需要扩容
newcap := old.cap //old.cap=>1
...
doublecap := newcap + newcap //2
if cap > doublecap {//cap=>5 实际的容量是5,大于2,新容量取值5
newcap = cap
}
//Sizeof A: 40
数据类型的size是40个字节
走switch的
default:
lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) //这里capmem=et.size*newcap即40*5=200
// 14 208 8192 39 80 8.12% capmem = roundupsize(capmem)//内存对齐向上取整 为208
newcap = int(capmem / et.size) //208/40=5
新容量为5
len=5,cap=5,ptr=0xc00005c420 2、看一下由1638扩容到2048的情况
先看结果
len=1638,cap=1638,ptr=0xc00005c420
len=1639,cap=2048,ptr=0xc00005c42
分析:
数据类型size=40
newcap := old.cap //先取用old.cap =>1638
doublecap := newcap + newcap //1638+1638=3276
if cap > doublecap { //cap是元素实际个数为1639
newcap = cap
} else {
if old.len < {//old.len=1638大于1024
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for < newcap && newcap < cap { //符合这个条件,扩容到1.25倍=>2047
newcap += newcap /
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= {
newcap = cap
}
}
}
.... default:
lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) //capmem=2047*40 =>81880
capmem = roundupsize(capmem) //内存对齐取整到81920
newcap = int(capmem / et.size) //81920/40=2048 //取整代码
func roundupsize(size uintptr) uintptr {
if size < _MaxSmallSize {
if size <= smallSizeMax- {
return uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-)/smallSizeDiv]])
} else {
return uintptr(class_to_size[size_to_class128[(size-smallSizeMax+largeSizeDiv-)/largeSizeDiv]])
}
}
if size+_PageSize < size {
return size
}
return round(size, _PageSize) //=>81920
} func round(n, a uintptr) uintptr {
return (n + a - ) &^ (a - )
} size是81880大于_MaxSmallSize = //源码中定义的常量 _PageShift =
_PageSize = << _PageShift //1<<13 =>8192
参考链接
https://www.jianshu.com/p/303daad705a3
slice扩容的更多相关文章
- Go slice 扩容机制分析
前言 我们都知道 Go 语言中的 slice 具有动态扩容的机制(不知道的同学请先补课 Go 切片) 但是其底层机制是什么呢?本着知其然,知其所以然的探索精神去研究一番.还不是为了应试 手动狗头 go ...
- 【Go】slice的一些使用技巧
原文链接:https://blog.thinkeridea.com/201901/go/slice_de_yi_xie_shi_yong_ji_qiao.html slice 是 Go 语言十分重要的 ...
- golang中,slice的几个易混淆点
slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀.不过,我感觉这个东西用的不好坑太多了.还是需要了解下他底层的实现 slice的结构定义 type slice s ...
- Go 语言入门 3-动态数组(slice)的特性及实现原理
go 语言中的动态数组(slice),是基于数组实现的,可以相比数组而言更加的灵活.其他语言的 slice 通常仅是一个 API, 但是 go 语言的 slice 不仅仅是一种操作, 也是一种数据结构 ...
- Go指南_切片的长度与容量
源地址 https://tour.go-zh.org/moretypes/11 一.描述 切片拥有 长度 和 容量. 切片的长度就是它所包含的元素个数. 切片的容量是从它的第一个元素开始数,到其底层数 ...
- golang一些知识点
2.冒泡排序(二维数组使用): func main() { i := 1 MYLABEL: for { i++ if i > 3 { break MYLABEL } } fmt.Println( ...
- 万级K8s集群背后etcd稳定性及性能优化实践
背景与挑战 随着腾讯自研上云及公有云用户的迅速增长,一方面,腾讯云容器服务TKE服务数量和核数大幅增长, 另一方面我们提供的容器服务类型(TKE托管及独立集群.EKS弹性集群.edge边缘计算集群.m ...
- go-slice实现的使用和基本原理
目录 摘要 Slice数据结构 使用make创建Slice 使用数组创建Slice Slice 扩容 Slice Copy 特殊切片 总结 参考 你的鼓励也是我创作的动力 Posted by 微博@Y ...
- 排查golang的性能问题 go pprof 实践
小结: 1.内存消耗分析 list peek 定位到函数 https://mp.weixin.qq.com/s/_LovnIqJYAuDpTm2QmUgrA 使用pprof和go-torch排查 ...
随机推荐
- Android CheckBox大小调整
关键是: android:scaleX="0.5" <CheckBox android:id="@+id/checkBox1" android:layou ...
- LGWR和DBWn的触发条件
Rolling Forward(前滚) Oracle启动实例并加载数据库,然后通过Online Redologs中的重做日志,重现实例崩溃前对数据库的修改操作.在恢复过程中对于已经提交的事务,但尚未写 ...
- centos7 yum安装mysql | mariaDb
mysql解释: mysql数据库是最常用的一种数据库,下面我来在centos7的迷你版上安装一下mysql.绝对纯净的环境哦 centos: CentOS-7-x86_64-Minimal-1 ...
- Django 路由系统URL 视图views
一.Django URL (路由系统) URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表:你就是以这种方式告诉Djan ...
- ES6-Function
Function 箭头函数 ES6中对于函数的扩展最吸引人的莫过于箭头函数啦,不多说,先学会再说. 函数体内的this对象,是定义时所在的对象,而不是使用时所在的对象,这个特性与正常函数不同. // ...
- spring boot(15)-异常处理
异常传递 如图:服务层和dao层的异常最终都会到达控制层,控制层的异常则会自动记入logback日志系统.所以我们应该在控制层来捕获系统异常 捕获控制层异常 import org.slf4j.Logg ...
- 转:java 委托
委托模式是软件设计模式中的一项基本技巧.在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理.委托模式是一项基本技巧,许多其他的模式,如状态模式.策略模式.访问者模式 ...
- 便利的操作plist文件
便利的操作plist文件 升级iOS9了,网络被强制切换成https了,你需要更新你的plist的文件才能够支持http,正常的做法是这样子的: 过程是不是挺繁琐的呢?如果你新建的另外一个工程,里面还 ...
- Python学习---django-debug-tools安装
[官网]http://django-debug-toolbar.readthedocs.io/en/1.2/installation.html [更多安装参考]http://blog.csdn.net ...
- matlab中关于函数句柄、feval函数以及inline函数的解析 (转)
http://blog.sina.com.cn/s/blog_7bff755b010180l3.html MATLAB函数句柄 函数句柄(Function handle)是MATLAB的一种数据类型. ...