golang 的内存占用是如何构成的呢?
unsafe.SizeOf()
转载:如何在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
也就是创建一个相同类型的初始化过的切片,并
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
}
标签:reflect,字节,forSize,vl,tp,golang,int,大小,Size
From: https://www.cnblogs.com/ayanmw/p/18174933