页首Html代码

返回顶部

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

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

posted @ 2024-05-06 14:12  ayanmw  阅读(321)  评论(0编辑  收藏  举报

页脚Html代码