Go中对象的大小

时间:2014-09-30 07:34:09

标签: go

只是考虑构建一个基于LRU的缓存机制,它可以识别内存消耗,因为我无法在搜索之后找到一个准备就绪的机制。缓存项是本机Go对象,可以是基本类型,结构,切片,数组或任何有效组合,但没有递归引用,我们可以为池分配一个较高的内存使用量,一旦总内存消耗达到阈值,将触发基于最近最近使用的清理。

我知道准确的内存大小计算是不实际的,但我认为粗略的估计可能会在这里提供很多帮助。至少它比项目编号更好,就像在GroupCache中完成的操作一样,忽略了缓存对象的大小。

那么计算/估算给定值使用的字节的正确方法是什么?

3 个答案:

答案 0 :(得分:2)

我很久以前写过这个函数,它的递归和避免经过了很多测试,但是它让你知道如何实现它:

var (
    sliceSize  = uint64(reflect.TypeOf(reflect.SliceHeader{}).Size())
    stringSize = uint64(reflect.TypeOf(reflect.StringHeader{}).Size())
)

func isNativeType(k reflect.Kind) bool {
    switch k {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
        reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
        reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
        return true
    }
    return false
}

func sizeofInternal(val reflect.Value, fromStruct bool, depth int) (sz uint64) {
    if depth++; depth > 1000 {
        panic("sizeOf recursed more than 1000 times.")
    }

    typ := val.Type()

    if !fromStruct {
        sz = uint64(typ.Size())
    }

    switch val.Kind() {
    case reflect.Ptr:
        if val.IsNil() {
            break
        }
        sz += sizeofInternal(val.Elem(), false, depth)

    case reflect.Struct:
        for i := 0; i < val.NumField(); i++ {
            sz += sizeofInternal(val.Field(i), true, depth)
        }

    case reflect.Array:
        if isNativeType(typ.Elem().Kind()) {
            break
        }
        sz = 0
        for i := 0; i < val.Len(); i++ {
            sz += sizeofInternal(val.Index(i), false, depth)
        }
    case reflect.Slice:
        if !fromStruct {
            sz = sliceSize
        }
        el := typ.Elem()
        if isNativeType(el.Kind()) {
            sz += uint64(val.Len()) * uint64(el.Size())
            break
        }
        for i := 0; i < val.Len(); i++ {
            sz += sizeofInternal(val.Index(i), false, depth)
        }
    case reflect.Map:
        if val.IsNil() {
            break
        }
        kel, vel := typ.Key(), typ.Elem()
        if isNativeType(kel.Kind()) && isNativeType(vel.Kind()) {
            sz += uint64(kel.Size()+vel.Size()) * uint64(val.Len())
            break
        }
        keys := val.MapKeys()
        for i := 0; i < len(keys); i++ {
            sz += sizeofInternal(keys[i], false, depth) + sizeofInternal(val.MapIndex(keys[i]), false, depth)
        }
    case reflect.String:
        if !fromStruct {
            sz = stringSize
        }
        sz += uint64(val.Len())
    }
    return
}

// Sizeof returns the estimated memory usage of object(s) not just the size of the type.
// On 64bit Sizeof("test") == 12 (8 = sizeof(StringHeader) + 4 bytes).
func Sizeof(objs ...interface{}) (sz uint64) {
    for i := range objs {
        sz += sizeofInternal(reflect.ValueOf(objs[i]), false, 0)
    }
    return
}

playground

数学可能有点偏差。

//编辑

修正了数学并推送到github以供将来参考。

答案 1 :(得分:0)

我不确定两者之间是否存在差异,但这里有“s” 两种方式,一种使用反射,另一种使用不安全的包。

package main 
import (
    "fmt"
    "reflect"
    "unsafe"
)

type T struct {
    a byte
    b int32
        c [1234]byte
        d float32   
}


func main(){
        var a T
    s1 := reflect.TypeOf(a)
    s2 := unsafe.Sizeof(T{})
    fmt.Printf("reflect.Sizeof(T) = %d, unsafe.Sizeof(T{} = %d)", s1.Size(), s2)
}

答案 2 :(得分:0)

这可能会有点晚,但可能会有帮助。 Sizeof()将返回大小。

package util

import (
    "math/cmplx"
    "reflect"
    "unsafe"
)

var Size []uint64

//Sizeof Function Will Find Approximate Size of Object in Bytes
func Sizeof(i interface{}) (size uint64) {
    size = 0
    sizeof(reflect.ValueOf(i), &size)
    return
}

//sizeof  private function which used to calculate size of object
func sizeof(val reflect.Value, sizeptr *uint64) {
    if val.Kind() >= reflect.Bool && val.Kind() <= reflect.Complex128 {
        (*sizeptr) += Size[val.Kind()]
        return
    }
    switch val.Kind() {
    case reflect.String:
        (*sizeptr) += Size[reflect.String] * uint64(val.Len())
    case reflect.Array:
        /*
            Then iterate through the array and get recursively call the size method.
            If all element hold the same value calculate size for one and multiply it with
            length of array. Wont wonk correctly if the array holds slices or any dynamic
            elements
        */
        for i := 0; i < val.Len(); i++ {
            sizeof(val.Index(i), (sizeptr))
        }

    case reflect.Interface:
        /*
            First we need to get the underlying object of Interface in golang using the Elem()
            And then we need to recursively call this function
        */
        temp := val.Elem()
        sizeof(temp, (sizeptr))

    case reflect.Map:
        for _, key := range val.MapKeys() {
            /*
                get the size of key by calling the size function
            */
            sizeof(key, (sizeptr))

            /*
                get the value pointed by the key and then recursively compute its size
            */
            mapVal := val.MapIndex(key)
            sizeof(mapVal, sizeptr)
        }

    case reflect.Ptr:
        prtVal := val.Elem()
        /*
            If the pointer is invalid or the pointer is nil then return without updating the size
        */
        if !prtVal.IsValid() {
            return
        }

        sizeof(val.Elem(), sizeptr)

    case reflect.Slice:
        for i := 0; i < val.Len(); i++ {
            sizeof(val.Index(i), sizeptr)
        }

    case reflect.Struct:
        /*
            Didn't consider the NumMethod here. Don't this that this is necessary or is it??
            Need to verify this...
        */
        for i := 0; i < val.NumField(); i++ {
            sizeof(val.Field(i), sizeptr)
        }

    case reflect.UnsafePointer:
        /*
            This allows conversion between elements. Dont think this should this is used in calculating
            size
        */
    case reflect.Func:
        // How to handle function pointers
    case reflect.Chan:
        // Don't think this case has to be handled as it will be produced and consumed
    default:
        return
    }
    return
}

func init() {
    Size = make([]uint64, 32)

    bool_val := true
    Size[reflect.Bool] = uint64(unsafe.Sizeof(bool(bool_val)))

    int_val := int(0)
    Size[reflect.Int] = uint64(unsafe.Sizeof(int_val))

    int8_val := int8(0)
    Size[reflect.Int8] = uint64(unsafe.Sizeof(int8_val))

    int16_val := int16(0)
    Size[reflect.Int16] = uint64(unsafe.Sizeof(int16_val))

    int32_val := int32(0)
    Size[reflect.Int32] = uint64(unsafe.Sizeof(int32_val))

    int64_val := int64(0)
    Size[reflect.Int64] = uint64(unsafe.Sizeof(int64_val))

    uint_val := uint(0)
    Size[reflect.Uint] = uint64(unsafe.Sizeof(uint_val))

    uint8_val := uint8(0)
    Size[reflect.Uint8] = uint64(unsafe.Sizeof(uint8_val))

    uint16_val := uint16(0)
    Size[reflect.Uint16] = uint64(unsafe.Sizeof(uint16_val))

    uint32_val := uint32(0)
    Size[reflect.Uint32] = uint64(unsafe.Sizeof(uint32_val))

    uint64_val := uint64(0)
    Size[reflect.Uint64] = uint64(unsafe.Sizeof(uint64_val))

    uintptr_val := uint64(0)
    Size[reflect.Uintptr] = uint64(unsafe.Sizeof(uintptr_val))

    float32_val := float32(0.0)
    Size[reflect.Float32] = uint64(unsafe.Sizeof(float32_val))

    float64_val := float64(0.0)
    Size[reflect.Float64] = uint64(unsafe.Sizeof(float64_val))

    complex64_val := complex64(cmplx.Sqrt(0 + 0i))
    Size[reflect.Complex64] = uint64(unsafe.Sizeof(complex64_val))

    complex128_val := complex128(cmplx.Sqrt(0 + 0i))
    Size[reflect.Complex128] = uint64(unsafe.Sizeof(complex128_val))

    string_val := string("0")
    Size[reflect.String] = uint64(unsafe.Sizeof(string_val))

}