惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
云风的 BLOG
云风的 BLOG
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Announcements
Recent Announcements
IT之家
IT之家
Google DeepMind News
Google DeepMind News
罗磊的独立博客
爱范儿
爱范儿
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
U
Unit 42
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
B
Blog
博客园 - 叶小钗
月光博客
月光博客
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
C
Check Point Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

博客园 - ayanmw

zhile的落幕: 我居然才知道2024年zhile就关停网站了,热心大佬暂时隐退 极度推荐: 9router 一个npm服务,可以让你将白嫖到的所有AI token以及付费API都集中到一起 开源项目介绍 OpenTeam 命令行 检测验证网站的 SSL证书 是否安全 Nginx 使用自签名 SSL 证书 关闭夸克浏览器在windows资源管理器中,图片右键菜单的万能转换开关 golang的defer 深坑 Windows Terminal 清屏方法 Ctrl+Shift+K 免费二级域名以及设置SSL证书和解析 docker加速镜像 golang + AI 写一个可以 一键让nas下载百度网盘链接的文件 的程序 raid 为什么而不可以两个硬盘交叉读写和交叉备份? google-protobuf库 在golang语言下的插件扩展 golang 空切片和nil切片 有区别吗? golang json库 忽略 omitempty Go语言: golang如何判断一个结构体的一个方法是匿名组合的,还是该结构体自己的方法? 吐槽 WPS 流氓行为: WPS 未经用户允许, 就建立了 WPS本地云盘 , 然后 云文档的文件 莫名其妙的的被删除了, 现在只能开会员恢复WPS云空间回收站的文件. 预测未来会有 内嵌AI大模型的游戏 好奇: windows10+都可以运行多个linux子系统了,为什么不支持运行多个windows子系统呢? gorm使用事务并发情况下切有最大mysql连接数限制的情况下的BUG,踩坑了 2024年 个人养老金 账户 应知应会 两个Mysql唯一索引的交换: 避免重复索引 Duplicate entry '3' for key 'priority_UNIQUE'
golang 获得一个结构体的字节大小
ayanmw · 2024-05-06 · via 博客园 - ayanmw

golang 的内存占用是如何构成的呢?

变量本身内存 以及 变量指针指向区域的内存 加起来,如果有包含关系,则应该递归获取内存大小.

unsafe.SizeOf()

一般可以获得变量本身的内存占用的,就用unsafe.SizeOf() 即可,可以获取基本类型: int int8,int16,int32,int64,string,[]int{},[1000]int{},*any,chan *any,func(any)any,type struct{},type interface{},等类型的本身.

转载:如何在Go中获取变量的内存大小?--CSDN问答

如果传递一个未初始化的变量,unsafe.Sizeof()与reflect.Type.Size()将只返回传递的变量的类型的大小,并不递归地遍历数据结构并增加所指向变量的大小。

切片是一个相对简单的结构体struct:reflect.SliceHeader,因为我们知道切片是引用一个备份的数组(或字符串——byte数组),我们可以简单地手动计算它的大小:

sliceint := make([]int32, 1000) //指向元素类型为int32的1000个元素的数组的切片
fmt.Println("Size of []int32:", unsafe.Sizeof(sliceint)) //24
fmt.Println("Size of [1000]int32:", unsafe.Sizeof([1000]int32{})) //4000
fmt.Println("Real size of s:", unsafe.Sizeof(sliceint)+unsafe.Sizeof([1000]int32{})) //4024
输出:

Size of []int32: 24
Size of [1000]int32: 4000
Real size of s: 4024
也就是创建一个相同类型的初始化过的切片,并

完美的做法

上述只有变量本身的空间占用,比如所有的指针都是8个字节,但是指针指向的空间呢? 所以我递归获取了变量的内存占用,下面是go函数的代码:
声明: 此代码还是不完善,测试用例 也需要构建完美用例.


func GetGoVarSize(val any) (sumSize int) {
	vl := reflect.ValueOf(val)
	tp := reflect.TypeOf(val)
	//tp.Size() + vl.Kind()
	switch vl.Kind() {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8,
		reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64,
		reflect.Complex128:
		return int(tp.Size()) //unsafe.Sizeof(val)
	case reflect.Map:
		forSize := int(tp.Size())
		for _, i := range vl.MapKeys() {
			forSize += GetGoVarSize(i.Interface()) + GetGoVarSize(vl.MapIndex(i).Interface())
		}
		return forSize
	case reflect.Array, reflect.Slice:
		forSize := int(tp.Size())
		for i := 0; i < vl.Len(); i++ {
			forSize += GetGoVarSize(vl.Index(i).Interface())
		}
		return forSize
		//return int(tp.Size()) + vl.Len()*GetGoVarSize(vl.Elem().Interface())//array and slice , not have Elem()
	case reflect.Chan:
		//请注意,尝试获取channel元素类型的操作(即varType.Elem())是不允许的,因为这将导致运行时的panic。在Go中,你不能通过反射来获取channel的元素类型。
		forSize := int(tp.Size())
		for i := 0; i < vl.Len(); i++ {
			forSize += 0
		}
		return forSize
	case reflect.Func:
		return int(tp.Size())
	case reflect.Interface:
		fallthrough
	case reflect.Pointer:
		forSize := int(tp.Size())
		if vl.IsNil() {
			return forSize
		}
		return forSize + GetGoVarSize(vl.Elem().Interface())
	case reflect.String:
		forSize := int(tp.Size())
		for i := 0; i < vl.Len(); i++ {
			forSize += GetGoVarSize(vl.Index(i).Interface())
		}
		return forSize
	case reflect.Struct:
		forSize := int(tp.Size())
		for i := 0; i < vl.NumField(); i++ {
			tpField := tp.Field(i)
			vlField := vl.Field(i)
			if tp.Name() == "Time" && tp.PkgPath() == "time" {
				_ = &time.Time{}
				//forSize += 8*2 + GetGoVarSize(time.Location{})
				//forSize += 8*2 + (16 + 5) + (24 + (8 + 3) + (8 + 1)) + (24 + (8 + 1 + 2)) + (16 + 0) + 8 + 8 + (8 + (3 + 8 + 1))
				forSize += 24 //time.Time{} 基本都是空数据,就用这个; 有 time.Local的才会用到上面的这个.
				continue
			}
			if !tpField.IsExported() {
				s := int(vlField.Type().Size()) //int(tpField.Type.Size())
				forSize += s
				log.Printf("Struct (%T)'s field(%v) not IsExported() string:%v, theSize=%v tp=%v vl=%v\n", val, i, vlField.String(), s, tpField, vlField)

				//TODO 不可导出字段,应该可以通过 unsafe 获取数据的. 不处理这个问题,无法获得真实的大小的.
				//var tp = reflect.New(tpField.Type)
				//tp.SetPointer(unsafe.Pointer(vlField.UnsafePointer()))
				//forSize += GetGoVarSize(tp.Interface())

				continue
			}
			if !vlField.CanInterface() {
				s := int(vlField.Type().Size())
				forSize += s
				log.Printf("Struct (%T)'s field(%v) cannot string:%v theSize use :%v\n", val, i, vlField.String(), s)
				continue
			}

			switch vlField.Kind() {
			case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.UnsafePointer,
				reflect.Interface, reflect.Slice:
				if vlField.IsNil() {
					forSize += int(vlField.Type().Size())
					continue
				}
			}
			forSize += GetGoVarSize(vlField.Interface())
		}
		return forSize
	case reflect.UnsafePointer:
		forSize := int(tp.Size())
		return forSize
	default:
		//return 0
		panic(fmt.Errorf(" %T Not support Kind %v", val, vl.Kind()))
	}
	return 0
}


其他

突然发现网络上 貌似有一个地方是我想要的 获取任何变量 实际占用内存大小的方法,做法跟我的类似:

https:// www. codenong .com/26975738/ # (这是一个爬虫网站,来源应该是 segmentfault.com 程序问答网站)
我已经编写了一个程序包,该程序可以计算运行时变量消耗的实际内存大小:
https://github.com/DmitriyVTitov/size

上述 貌似是Part of the Transflow Project

但是我的会计算更多字节,这个可能需要结合起来去处理.