欢迎大家来到 『Go 面试』 专题,这个专栏每篇都会分享一个面试中可能会考的Go面试题,希望帮大家面试不再发怵。

本篇问题:slice 扩容后容量及内存如何计算?

1. 扩容后预估容量

假设现在有一个长度为 2 的切片,对其进行扩容,增加三个元素

sli := []int{1,2}
sli = append(sli, 3, 4, 5)

对于扩容后的切片,长度为 5,这一点没有任何争议。

但容量呢?难道也是 5?

经过运行验证,实际的容量为 6 。

什么情况?这 6 是如何计算出来的呢?

这就不得不去 Go 源代码中一探究竟,在 runtime/slice.go 关于 slice 扩容增长的代码如下:

newcap := old.cap
if newcap+newcap < cap {newcap = cap
} else {for {if old.len < 1024 {newcap += newcap} else {newcap += newcap / 4}if newcap >= cap {break}}
}

对于这段代码,只要理解前两行,其他的就不攻自破了

  • 第一行的 old.cap:扩容前的容量,对于此例,就是 2

  • 第二行的 cap:扩容前容量加上扩容的元素数量,对于此例,就是 2+3

整段代码的核心就是要计算出扩容后的预估容量,也就是 newcap,计算的具体逻辑是:

  1. 若 old.cap * 2  小于 cap,那 newcap 就取大的 cap

  2. 若 old.cap * 2 大于 cap,并且old.cap 小于 1024,那 newcap 还是取大,也即 newcap = old.cap * 2

  3. 若 old.cap * 2 大于 cap,但是old.cap 大于 1024,那两倍冗余可能就有点大了,系数换成 1.25,也即 newcap = old.cap *  2

但 newcap 只是预估容量,并不是最终的容量,要计算最终的容量,还需要参考另一个维度,也就是内存分配。

2. 内存的分配规律

举个现实中的例子来说

你家里有五个人,每个人都想吃绿豆糕,因此你的需求就是 5,对应上例中的 cap ,于是你就到超市里去买。

但超市并不是你家开的,绿豆糕都是整盒整盒的卖,没有卖散的,每盒的数量是 6 个,因此你最少买 6 个。

每次购买的最少数量,就可以类比做 Go 的内存分配规律。

只有了解了 Go 的内存分配规律,我们才能准确的计算出我们最少得买多少的绿豆糕(得申请多少的内存,分配多少的容量)。

关于内存管理模块的代码,在 runtime/sizeclasses.go

// class  bytes/obj  bytes/span  objects  tail waste  max waste
//     1          8        8192     1024           0     87.50%
//     2         16        8192      512           0     43.75%
//     3         32        8192      256           0     46.88%
//     4         48        8192      170          32     31.52%
...
//    17        256        8192       32           0      5.86%
//    18        288        8192       28         128     12.16%
//    19        320        8192       25         192     11.80%
//    20        352        8192       23          96      9.88%
//    21        384        8192       21         128      9.51%
//    22        416        8192       19         288     10.71%
//    23        448        8192       18         128      8.37%
//    24        480        8192       17          32      6.82%
//    25        512        8192       16           0      6.05%
...
//    66      32768       32768        1           0     12.50%

从上面这个表格中,可以总结出一些规律。

  • 在小于16字节时,每次以8个字节增加

  • 当大于16小于2^8时,每次以16字节增加

  • 当大于2^8小于2^9时以32字节增加

  • 依此规律…

3. 匹配到合适的内存

第一节中我们例子中,主人公是一个元素类型为 int 的切片,每个 int 占用为 8 个字节,由于我们计算出的 newcap 为 5,因此新的切片,最少最少要占用 5*8 = 40 个字节。

再到第二节中的表格中查看,发现离 40 byte 最接近的是 32 和 48 两个档位。

如果是 32 byte,就是不够用了,

因此 只能选择 48 这个档位去分配内存。

有了实际分配的内存,再反回去计算容量,就是扩容后真实的切片容量,也就是 48/8 = 6

是不是很简单呢?

本系列的所有文章,我都开放到 Github 上:https://github.com/iswbm/golang-interview

如果文章有写得不对的地方,可以去那里提交 issue 帮我指正。顺便可以帮我点个小 ⭐⭐,在那里我对题库进行了分类整理,方便索引查找。

加油噢,我们下篇见!

- END -

扫码关注公众号「网管叨bi叨」

给网管个星标,第一时间吸我的知识 

Go 面试专题 | slice 扩容后的内存容量如何计算?相关推荐

  1. golang slice扩容机制

    Slice expanse capacity slice这种数据结构便于使用和管理数据集合,可以理解为是一种动态数组,slice也是围绕动态数组的概念来构建的.既然是动态数组,那么slice是如何扩容 ...

  2. Go 函数的 Map 型参数,会发生扩容后指向不同底层内存的事儿吗?

    最近跟同事做项目,由于要在函数里向一个 Map 中写入不少数据,这个 Map 是作为参数传到函数里的.他问了我一个问题: "如果把 Map 作为函数参数传递,会不会像用 Slice 做参数时 ...

  3. hashmap中用红黑树不用其他树_HashMap面试专题:常问六题深入解析

    引言 其实我很早以前就想写一篇关于HashMap的面试专题.对于JAVA求职者来说,HashMap可谓是集合类的重中之重,甚至你在复习的时候,其他集合类都不用看,专攻HashMap即可. 然而,鉴于网 ...

  4. 2021最新腾讯面经分享:知识大纲+技术文档+面试专题

    背景 985毕业至今刚好一年,我曾做过两三个月的测试感觉不是很合适,后面选择从事后端开发,还挺香.现在已经进入秋招的提前批了,想着去大厂试试水,就去了腾讯,整个一面下来我整个人都傻了,表示怀疑人生.. ...

  5. php面试专题---MYSQL查询语句优化

    php面试专题---MYSQL查询语句优化 一.总结 一句话总结: mysql的性能优化包罗甚广: 索引优化,查询优化,查询缓存,服务器设置优化,操作系统和硬件优化,应用层面优化(web服务器,缓存) ...

  6. 精心整理全网最全Tomcat面试专题及答案(共19题,含答案解析),tomcat面试看这篇就够了!

    [Java架构师面试网]收集整理了一些Java面试的常见问题,这些问题可能会在你下一次技术面试中遇到.想成为Java架构师,这些都是不可避免也是必须要掌握的哦,对于其他模块的面试题,我后续也将单独分享 ...

  7. 【Java架构师面试题】设计模式面试专题(共35题含答案)

    设计模式(DesignPattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路.它不是语法规定,而是一套用来提高代码可复用性.可维护性.可读性.稳健性以及安全性的解决方案. 本篇为设计 ...

  8. php面试专题---MySQL常用SQL语句优化

    php面试专题---MySQL常用SQL语句优化 一.总结 一句话总结: 原理,万变不离其宗:其实SQL语句优化的过程中,无非就是对mysql的执行计划理解,以及B+树索引的理解,其实只要我们理解执行 ...

  9. HTTP协议——面试专题

    HTTP协议--面试专题 HTTP协议--面试专题 HTTP协议--面试专题 HTTP协议--面试专题 前言 一.http状态码和method知识点考查 1.常见的Http Method有哪些,使用场 ...

最新文章

  1. 清空SQL Server数据库日志的SQL语句
  2. C语言中do...while(0)用法小结
  3. QFIL工具如何导出手机分区数据
  4. 近三月浏览器网页访问量统计
  5. 慕尼黑工业大学最新综述:深度神经网络中的不确定性
  6. 最大化窗口设置_BetterTouchTool的几个实用设置
  7. bash shell 循环读入每一行(转)
  8. 虚拟机几种网络连接方式的区别
  9. [html] 页面刷新时sessionStroage会变(会清空)吗?
  10. Linux内核奔溃分析
  11. python Linux系统信息
  12. HTML5-A*寻路算法2
  13. 华为海外女科学家为您揭秘:GaussDB(for MySQL)云栈垂直集成的力量有多大?
  14. Iplat62---CRUD
  15. html5应用开发大赛
  16. SSM(Spring+springMVC+MyBatis)框架-springMVC实现图片上传
  17. python平稳性检验程序_用python处理时间序列数据,检验平稳性跟纯随机性
  18. DSN格式转SCH格式
  19. 敏捷史话(一):用一半的时间做两倍的事——Scrum之父Jeff Sutherland的传奇人生
  20. RabbitMq报错 Execution of Rabbit message listener failed

热门文章

  1. 限制UI只能在屏幕内移动(放大或缩小屏幕同样适用)
  2. vue练习之vue+cnode api
  3. appium===安卓SDK下载很慢的解决办法
  4. 使用Spring配合Junit进行单元测试的总结
  5. MapReduce: map读取文件的过程
  6. [跟我学中小企业架构部署]之八:备份服务器部署
  7. www.cnblog.org无法访问了
  8. angualr Material Icons
  9. Docker pull镜像报错问题
  10. windows中端口号被占用的解决方法