golang 获得一个结构体的字节大小
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
但是我的会计算更多字节,这个可能需要结合起来去处理.
------------------------------------------------------------------------------------------------
一定要专业!本博客定位于 ,C语言,C++语言,Java语言,Android开发和少量的Web开发,之前是做Web开发的,其实就是ASP维护,发现EasyASP这个好框架,对前端后端数据库 都很感觉亲切啊。. linux,总之后台开发多一点。以后也愿意学习 cocos2d-x 游戏客户端的开发。