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排查 ...
随机推荐
- 3.Spring MVC return url问题总结
一.return "cartSuccess" 和 return "redirect:/cart/cart.html" 的区别 二.return modelAnd ...
- Ssh 证书验证 续篇
今天下午正好有外面的人要登录服务器,想了想,普通用户密码就是不想给,然后我就这样做了. useradd alex ---创建账户和密码 passwd alex mkdir /home/alex/.ss ...
- SQL Server FOR XML PATH 和 STUFF函数的用法
FOR XML PATH ,其实它就是将查询结果集以XML形式展现,将多行的结果,展示在同一行. 下面我们来写一个例子: 假设我们有个工作流程表: CREATE TABLE [dbo].[Workfl ...
- REST Framework 的分页
分页: PageNumberPagination from rest_framework.pagination import PageNumberPagination 导入分页之后你要实例化分页这个对 ...
- rest framework 的权限管理
下面是对单个的视图进行的设置的: 请求的时候用postman然后发送信息 我们下面所有的举例都是在用户对Comment这个表的操作 首先先生成一个类似于cookie的字符串 发送给前端浏览器 然后下次 ...
- 精简计算UITableView文本高度
精简计算UITableView文本高度 本人视频教程系类 iOS中CALayer的使用 最终效果: 核心源码(计算文本高度的类) NSString+StringHeight.h 与 NSStrin ...
- 也许,这样理解HTTPS更容易
http://kb.cnblogs.com/page/563885/ 本文尝试一步步还原HTTPS的设计过程,以理解为什么HTTPS最终会是这副模样.但是这并不代表HTTPS的真实设计过程.在阅读本文 ...
- 铁乐学Python_Day33_网络编程Socket模块1
铁乐学Python_Day33_网络编程Socket模块1 部份内容摘自授课老师的博客http://www.cnblogs.com/Eva-J/ 理解socket Socket是应用层与TCP/IP协 ...
- 解决Failed to load the JNI shared library xxx/xxx/jvm.dll 错误
原因:jdk发生变化(新装了32位jdk),eclipse在启动时使用了 系统环境变量中的jdk路径(32位). 解决:只要把旧的64位的jre路径指定给eclipse启动文件即可. 在eclipse ...
- Hadoop HBase概念学习系列之概念视图(又名为逻辑模型)(八)
其实啊,我们把HBase想象成一个大的映射关系,再者,本来,HBase存储的数据可以理解为一种key和value的映射关系,但有不是简简单单的映射关系那种,因为比如有各个时间戳版本啊. 通过行键.行键 ...