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排查 ...
随机推荐
- mysql navicat 及命令行 创建、删除数据库
1.命令行创建数据库 create database mybatis default character set utf8 collate utf8_general_ci; drop database ...
- LeetCode 题解之Minimum Index Sum of Two Lists
1.题目描述 2.问题分析 直接是用hash table 解决问题 3.代码 vector<string> findRestaurant(vector<string>& ...
- Fatal error: Can't use function return value in write context
这个的出错原因很简单,先贴出错代码: <?php $contact = array("id"=>1, "姓名"=>"老高" ...
- poj2182 逆推暴力
题意 告诉有n头牛,每头牛有一个编号1~n,再一次烂醉之后,奶牛们没有按照编号排队:告诉你对于第i头奶牛,在它之前有多少头奶牛比它的编号小(i>1,因为第1头奶牛的数据永远为0,故题中省略),求 ...
- 【Kettle】3、数据源连接配置
1.各系统版本信息 System:Windows旗舰版 Service Pack1 Kettle版本:6.1.0.1-196 JDK版本:1.8.0_72 2.连接介绍 关于Kettle数据源连接方式 ...
- 使用CoreData [2]
使用CoreData [2] 此篇讲解CoreData处理关系型数据. 1. 先创建出Student于Teacher的实体. 2. 确定关系,并修改描述 3. 创建对象,并查看一下关系(Teacher ...
- 下拉刷新对象RefreshObject
下拉刷新对象RefreshObject 效果 说明 1. 分离了动画实现与刷新逻辑 2. 你可以根据自己的需要,设计自己的动画效果,你的动画只需要继承协议,实现协议里面的方法即可 3. 本设计方案是用 ...
- UNIX高级环境编程(6)标准IO函数库 - 流的概念和操作
标准IO函数库隐藏了buffer大小和分配的细节,使得我们可以不用关心预分配的内存大小是否正确的问题. 虽然这使得这个函数库很容易用,但是如果我们对函数的原理不熟悉的话,也容易遇到很多问题. 1 ...
- 铁乐学python_day18-19_面向对象编程1
以下笔记绝大部分(百分之80或以上)摘自我的授课老师之一:老男孩教育中的景老师. 她上课讲的知识点由浅入深,引人入胜,听她的课完全不会感觉到困阿,而且不知不觉中就感觉掌握了. 她的博客是: http: ...
- December 04th 2016 Week 50th Sunday
Learn wisdom by the follies of others. 前车之鉴,后人之师. Maybe my personal state is that others can learn w ...