go size class 内存块思考

news2024/11/14 17:04:37

浏览到的一篇文章,让我也有机会反思一下 go 内存管理。网络上,go 内存管理方面的介绍挺多的,面试的时候,偶尔也会被问到内存管理。

而且,从 go1.15 到 go1.16 在 size class 上引入了新的内存块,能直观的看出来这种变化,也很有必要记录一下。

类型 size

我们先来回顾一下,go 类型的内存大小,以几个通用的数据类型举例,string 类型占用 16 字节,[]string 字符串切片占用 24 个字节,interface{} 占用 16 个字节。

为什么 interface{} 占用 16 个字节呢,因为 interface 在内存中的数据类型如下。我理解,eface 是 empty interface 的简称,表示没有方法类型。eface 包含两个成员属性,都是指针类型,而指针类型占用 8 个字节,不需要额外的 padding 内存,所以,interface{} 占用 16 个字节

type eface struct {
	_type *_type
	data  unsafe.Pointer
}

当然,go 也提供了计算类型占用内存大小的方法,我们可以通过类型声明来直接计算出类型的尺寸大小。下面的 Sizeof 就被用来计算类型的内存大小。

这样计算出来只是类型的内存大小,实际存储数据的内存大小并不会被计算出来。实际存储数据的大小如何计算呢?

拿 []string 的类型来说明,通过 Sizeof 计算的结果是 24 字节,但实际的数据占用内从并没有被统计进去,如果 []string 的元素中包含 100字节的字符串,如何把这部分数据也计算出来呢?

目前来看,确实没有什么好的办法,只能按个元素计算内存大小了。

func main() {
	var a interface{}
	size := unsafe.Sizeof(a)
	fmt.Println(size)
}

接口类型

接口类型包含两份数据,一份是类型数据,一份是数据本身。将[]string 类型转换赋值给 interface 类型,和赋值给[]string 类型相比,虽然变量 a 类型占用了 16 字节,变量 b 类型占用了 24 字节,但实际上,interface{} 花费了更多的内存。

主要的原因是指针类型,无论实际的元素占用多个的内存大小,只要是转换为指针类型,也都只占用 8 个字节的大小。

func main() {

	src := []string{}

	var a interface{} = src
	var b []string = src
}

我们看一下 eface 中 _type 的类型声明,避免程序中无效的 interface 类型转换,也是提高程序性能的一种手段。

在这里插入图片描述

go1.16 新特性

我们先来看一个例子,使用 benchmark 性能压测 sort.Strings 性能。

sort.Strings 是字符串排序的方法,对切片元素按照增序进行排序。这个排序过程也涉及到 interface 类型的转换操作。
另外,在第一次排序完成之后,变量 s 其实就已经有序了,这个性能压测的输入并不是随机的。

var s = []string{"heart", "lungs", "brain", "kidneys", "pancreas"}

func BenchmarkName(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sort.Strings(s)
	}
}

我们首先在 go 1.16 的环境下,执行性能测试,执行3次,每次输出内存申请的信息,执行结果如下:

在这里插入图片描述

作为对比,我们切换到 go1.15 的环境下做同样的性能测试:

除了截图上明确标红的部分,在这个问题上,go1.16 比 go1.15 性能上有了一点点的性能提升。

在这里插入图片描述
下面的重点落在了 go1.16每次申请的内存是 24 B/op,而go1.15是 32 B/op。我们可以从 /runtime/sizeclasses.go 了解到个变化,

下面是 go1.15的源码

/ Code generated by mksizeclasses.go; DO NOT EDIT.
//go:generate go run mksizeclasses.go

package runtime

// 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%
//     5         64        8192      128           0     23.44%
//     6         80        8192      102          32     19.07%
//     7         96        8192       85          32     15.95%
//     8        112        8192       73          16     13.56%
//     9        128        8192       64           0     11.72%

下面是go1.16的源码,比较明显,go1.16增加了 24 bytes 的 class。

每个 span 的大小是 8192,24bytes 的 class,每个 span 可以申请 341 个。剩余的 8 byte 就必须浪费了。

最大的浪费比率,只能是假设的这种情况,内存中对于 17 bytes 大小的对象,只能将它存储到 class 为3 的对象中,这就导致 24 bytes 中有 7 bytes 会被浪费掉,总共可以申请 341 个对象,总共浪费 341 *7 +8 = 2395 bytes,占比 29.24%

不过,要构造类型大小 17 bytes 的对象,在业务代码中基本不会存在,因为有内存对齐的逻辑存在。

// Code generated by mksizeclasses.go; DO NOT EDIT.
//go:generate go run mksizeclasses.go

package runtime

// 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         24        8192      341           8     29.24%
//     4         32        8192      256           0     21.88%
//     5         48        8192      170          32     31.52%
//     6         64        8192      128           0     23.44%
//     7         80        8192      102          32     19.07%
//     8         96        8192       85          32     15.95%
//     9        112        8192       73          16     13.56%

内存申请流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/357616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信奥一本通1365

1365&#xff1a;FBI树(fbi) 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 6443 通过数: 4366 【题目描述】 我们可以把由“0”和“1”组成的字符串分为三类&#xff1a;全“0”串称为B串&#xff0c;全“1”串称为I串&#xff0c;既含“0”又含“1”的串则称为…

C语言——动态内存管理(详解)

1.动态内存管理存在的意义 在前面的C语言的学习中&#xff0c;我们已经掌握的空间开辟的方式有如下两种 int i 0; //开辟了4字节大小的空间存放i int arr[5] {0}; //开辟了20字节的空间存放数组arr这样开辟空间有两个显著的特点&#xff1a; 1、每次开辟空间的大小都是固定…

K8S集群将Docker切换到Containerd

文章目录1. 开启节点维护1.1 将节点设置成不可调度1.2 驱逐节点上的 Pod1.3 停止相关服务2. 升级到 containerd2.1 安装 containerd2.2 调整 containerd 配置2.3 修改 kubelet 启动配置参数3. 重启节点服务4. 验证升级后的节点5. 容器管理工具5.1 容器管理命令行工具对比5.2 cr…

【JavaScript】ES6模块化与异步编程高级用法

一、ES6模块化 1、回顾&#xff1a;node.js遵循了ComminJS的模块化规范。 其中&#xff1a; 导入其他模块使用require()方法模块对外共享成员使用module.exports对象 模块化的好处&#xff1a; 遵守同样的模块化规范写代码&#xff0c;降低了沟通成本&#xff0c;极大方便了…

使用 ApiPost进行WebService 调试,就是这样简单

文章目录5.1 新建一个 HTTP 接口5.2 添加请求Body5.3 设置请求Header5.4 发送请求Apipost 可以用于调试 WebService 请求。具体步骤如下&#xff1a;5.1 新建一个 HTTP 接口 新建一个 HTTP接口&#xff0c;URL 部分填写 endpoint&#xff0c;请求方式选择 POST。以下 URL 为示…

SpringBoot自动装配的原理

前言 在开发SpringBoot项目时&#xff0c;当我们引入spring-boot-starter-xxx依赖后&#xff0c;想要使用依赖中的bean&#xff0c;直接就用Autowired拿来用了&#xff0c;不需要用xml或者注解的方式把它先注入到Spring容器中。这就是自动装配的特性&#xff0c;本文来讲述Spri…

Spring Bean循环依赖

解决SpringBean循环依赖为什么需要3级缓存&#xff1f;回答&#xff1a;1级Map保存单例bean。2级Map 为了保证产生循环引用问题时&#xff0c;每次查询早期引用对象&#xff0c;都拿到同一个对象。3级Map保存ObjectFactory对象。数据结构1级Map singletonObjects2级Map earlySi…

CMake option选项使用方式及注意事项

CMAKE官网 &#x1f358; 在复习 CMake 的时候&#xff0c;使用了 option 功能&#xff0c;发现修改了参数的值之后&#xff0c;和未修改的效果一样&#xff0c;然后不断的查找 option 的使用方法&#xff0c;最后发现并非 option 使用方式而错误&#xff0c;而是 option 第一…

SpringCloudAlibaba-分布式事务Seata

一、介绍官网&#xff1a;http://seata.io/zh-cn/index.html TC (Transaction Coordinator) - 事务协调者维护全局和分支事务的状态&#xff0c;驱动全局事务提交或回滚。TM (Transaction Manager) - 事务管理器定义全局事务的范围&#xff1a;开始全局事务、提交或回滚全局事务…

Mac Appium iOS自动化测试环境搭建教程

目录Appium环境搭建Mac iOS环境搭建Appium基础Appium进阶环境搭建安装brewCopyruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"安装javaCopybrew install java安装python3 及相关包Copybrew install python3 pip install selenium pip install app…

实现8086虚拟机(四)——mov 和 jmp 指令解码

文章目录mov 指令解码jmp 指令解码这篇文章举例来讲讲 mov 指令和 jmp 指令解码函数的实现&#xff0c;其他的指令解码函数都与这些类似。mov 指令解码 以 mov 指令中的一类&#xff1a;寄存器/内存 到/从 寄存器&#xff0c;来详细说明解码函数的实现。 机器指令格式如下&am…

联想M7268激光打印机开机红绿灯双闪报错不打印

故障现象: 一台联想M7268激光打印机开机后电源键、复印键一起双闪,电源键闪红灯、复印键闪绿灯; 检测维修: 根据闪灯故障判断如果无卡纸异常情况下可能是激光器故障,因为以前曾经维修过一台一模一样的机器故障基本相同,先打开机器吧,把硒鼓拿出来先看看有没有卡纸,进纸…

php小程序餐馆点餐订餐外卖系统

目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2.2MyEclipse环境配置 4 2.3 B/S结构简介 4 2.4MySQL数据库 5 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3.1.2技术可行性 6 3.1.3运行可行性 6 …

c++11 标准模板(STL)(std::unordered_set)(二)

定义于头文件 <unordered_set> template< class Key, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator<Key> > class unordered_set;(1)(C11 起)namespace pmr { templ…

python中的for循环以及枚举函数enumerate()

一、可迭代的对象&#xff08;iteratle_object&#xff09; python中可以使用for循环进行迭代的对象大致有以下几种类型&#xff1a; String(字符串)List(列表)Tuple(元组)Dictionary(字典)range()内置函数返回的对象 二、for循环迭代示例 1. 依次输出字符串"python&q…

printk浅析

内核printk原理介绍 - 知乎 (zhihu.com)34.Linux-printk分析、使用prink调试驱动 (bbsmax.com)【原创】计算机自制操作系统(Linux篇)五&#xff1a;内核开发之万丈高楼从地起---printk(理清pintf/vprintf&#xff1b;sprintf/vsprintf &#xff1b;fprintf/vfprintf) - 知乎 (z…

自抗扰控制ADRC之扩张观测器

目录 前言 1. 被控对象(被观测对象) 2.非线性观测器 2.1仿真分析 2.2仿真模型 2.3仿真结果 3.线性观测器 3.1仿真模型 3.2仿真结果 4.总结和学习问题 前言 什么叫观测器&#xff1f;为什么该类观测称为扩张观测器&#xff1f; &#xff1a;观测器可以理解为所观测…

组合数学原理与例题

目录 一、前言 二、计数原理 1、加法原理 2、分割立方体&#xff08;lanqiaoOJ题号1620&#xff09; 3、乘法原理 4、挑选子串&#xff08;lanqiaoOJ题号1621&#xff09; 5、糊涂人寄信&#xff08;lanqiaoOJ题号1622&#xff09; 6、战斗吧N皇后&#xff08;lanqiaoO…

依次判断数组1对中的每个元素是否小于等于数组2中对应位置的每个元素numpy.less_equal()

【小白从小学Python、C、Java】【计算机等级考试500强双证书】 【Python-数据分析】 依次判断数组1对中的每个元素是否 小于等于数组2中对应位置的每个元素 numpy.less_equal() [太阳]选择题 以下错误的一项是? import numpy as np a np.array([1,2,3]) b np.array([1,3,2]) …

kubernetes 核心技术-Pod(1)

概述&#xff1a; 首先要知道 Pod 不是容器&#xff01; 一、 基本概念 Pod 是 k8s 系统中可以创建和管理的最小单元。k8s 不会直接处理容器&#xff0c;而是podpod 包含多个容器(一组容器的集合)一个pod中容器共享网络命名空间pod是短暂的(生命周期) 二、Pod存在的意义 创建…