我目前正在尝试获取 Go 中复杂结构的大小。
我已经阅读过使用
reflect
和 unsafe
的解决方案,但是这些解决方案都无法帮助包含数组或映射(或指向底层数据结构的指针的任何其他字段)的结构。
示例:
type testStruct struct {
A int
B string
C struct{}
items map[string]string
}
如果
items
中包含一些值,我如何找出上面的正确字节大小?
通过使用反射包,您可以非常接近结构及其内容所需的内存量。您需要迭代字段并获取每个字段的大小。例如:
func getSize(v interface{}) int {
size := int(reflect.TypeOf(v).Size())
switch reflect.TypeOf(v).Kind() {
case reflect.Slice:
s := reflect.ValueOf(v)
for i := 0; i < s.Len(); i++ {
size += getSize(s.Index(i).Interface())
}
case reflect.Map:
s := reflect.ValueOf(v)
keys := s.MapKeys()
size += int(float64(len(keys)) * 10.79) // approximation from https://golang.org/src/runtime/hashmap.go
for i := range(keys) {
size += getSize(keys[i].Interface()) + getSize(s.MapIndex(keys[i]).Interface())
}
case reflect.String:
size += reflect.ValueOf(v).Len()
case reflect.Struct:
s := reflect.ValueOf(v)
for i := 0; i < s.NumField(); i++ {
if s.Field(i).CanInterface() {
size += getSize(s.Field(i).Interface())
}
}
}
return size
}
这使用 Reflect 获取 v 的大小,然后对于本示例中支持的类型(切片、映射、字符串和结构),它计算存储在其中的内容所需的内存。您需要在此处添加您需要支持的其他类型。
还有一些细节需要解决:
对于第二点,您可以在处理结构时进行递归调用之前过滤掉它们,您可以在 reflect 包的文档中检查类型。
扩展上面的答案,如果你想获取指针对象的大小,那么我们需要首先取消引用它,如下所示,因为它被取消引用,我们还必须将类型更改为结构体,
func getSize(v interface{}) int {
size := int(reflect.TypeOf(v).Size())
kind := reflect.TypeOf(v).Kind()
//dereferencing begins
if kind == reflect.Pointer {
s := reflect.ValueOf(v)
if !s.IsNil() {
v = reflect.ValueOf(v).Elem().Interface()
kind = reflect.TypeOf(v).Kind()
}
}
switch kind {
case reflect.Slice:
s := reflect.ValueOf(v)
for i := 0; i < s.Len(); i++ {
size += getSize(s.Index(i).Interface())
}
case reflect.Map:
s := reflect.ValueOf(v)
keys := s.MapKeys()
size += int(float64(len(keys)) * 10.79) // approximation from https://golang.org/src/runtime/hashmap.go
for i := range keys {
size += getSize(keys[i].Interface()) + getSize(s.MapIndex(keys[i]).Interface())
}
case reflect.String:
size += reflect.ValueOf(v).Len()
case reflect.Struct:
s := reflect.ValueOf(v)
for i := 0; i < s.NumField(); i++ {
if s.Field(i).CanInterface() {
size += getSize(s.Field(i).Interface())
}
}
case reflect.Interface:
s := reflect.ValueOf(v)
for i := 0; i < s.NumField(); i++ {
size += getSize(s.Field(i).Interface())
}
}
return size
}