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

推荐订阅源

H
Help Net Security
S
Secure Thoughts
I
Intezer
Project Zero
Project Zero
Stack Overflow Blog
Stack Overflow Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
F
Full Disclosure
P
Proofpoint News Feed
T
The Exploit Database - CXSecurity.com
人人都是产品经理
人人都是产品经理
博客园_首页
J
Java Code Geeks
Recorded Future
Recorded Future
K
Kaspersky official blog
GbyAI
GbyAI
S
Schneier on Security
The Cloudflare Blog
Spread Privacy
Spread Privacy
C
Cisco Blogs
The Hacker News
The Hacker News
博客园 - 三生石上(FineUI控件)
H
Hackread – Cybersecurity News, Data Breaches, AI and More
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
爱范儿
爱范儿
Microsoft Azure Blog
Microsoft Azure Blog
Know Your Adversary
Know Your Adversary
T
Tenable Blog
A
Arctic Wolf
Blog — PlanetScale
Blog — PlanetScale
H
Hacker News: Front Page
The Last Watchdog
The Last Watchdog
O
OpenAI News
Last Week in AI
Last Week in AI
B
Blog RSS Feed
T
Troy Hunt's Blog
G
GRAHAM CLULEY
N
Netflix TechBlog - Medium
Vercel News
Vercel News
量子位
The Register - Security
The Register - Security
Google Online Security Blog
Google Online Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
U
Unit 42
Security Archives - TechRepublic
Security Archives - TechRepublic
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News and Events Feed by Topic

博客园 - ayanmw

极度推荐: 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

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