Go语言中的对象大小

huangapple go评论86阅读模式
英文:

Size of Object in Go

问题

只是考虑构建一个基于LRU的缓存机制,该机制应该能够考虑内存消耗,因为在搜索后我找不到现成的解决方案。缓存的项目是原生的Go对象,可以是基本类型、结构体、切片、数组或任何有效的组合,但不能有递归引用。我们可以为池分配一个上限的内存使用量,一旦总内存消耗达到阈值,就会触发基于最近最少使用的清理。

我知道准确计算内存大小是不现实的,但我认为粗略估计可能会在这里有所帮助。至少它比像GroupCache中所做的仅仅计算项目数量要好,而忽略了缓存对象的大小。

那么,有什么适当的方法来计算/估计给定值使用的字节数呢?

英文:

Just thinking of building an LRU based caching mechanism which is memory consumption aware as I can't find a ready one after some searching. The cached item is native Go object which can be basic type, struct, slice, array or any valid combination, but without recursive reference, and we can assign a upper limited of memory usage to the pool and once the total memory consumption reach a threshold, a cleanup based on Latest Recently Used would be triggered.

I understand accurate memory size calculation is not practical but I think a rough estimation may do lot help here. At least it's better then item number counting like what's done in GroupCache ignoring size of the cached object.

So what's a proper way to calculate/estimate the bytes used by given value?

答案1

得分: 4

我很乐意为您提供翻译。以下是您提供的代码的翻译:

我很久以前写了这个函数它是递归的没有经过太多测试但可以给您一个实现的思路

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返回对象的估计内存使用情况,而不仅仅是类型的大小。
// 在64位系统上,Sizeof("test") == 12(8 = sizeof(StringHeader) + 4字节)。
func Sizeof(objs ...interface{}) (sz uint64) {
	for i := range objs {
		sz += sizeofInternal(reflect.ValueOf(objs[i]), false, 0)
	}
	return
}

// 编辑

修正了数学问题并将其推送到GitHubhttps://github.com/OneOfOne/go-utils/tree/master/memory)以供将来参考。

您可以在playground上运行此代码。

请注意,这只是一个翻译,我无法回答关于代码功能或正确性的问题。

英文:

I wrote this function a long time ago, it's recursive and haven't been tested much, but it gives you an idea on how to implement it:

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 &gt; 1000 {
panic(&quot;sizeOf recursed more than 1000 times.&quot;)
}
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 &lt; 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 &lt; 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 &lt; 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()) &amp;&amp; isNativeType(vel.Kind()) {
sz += uint64(kel.Size()+vel.Size()) * uint64(val.Len())
break
}
keys := val.MapKeys()
for i := 0; i &lt; 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(&quot;test&quot;) == 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
}

<kbd>playground</kbd>

<strike>The math might be a tiny bit off.</strike>

// edit

Fixed the math and pushed to github for future references.

答案2

得分: 0

我不确定这两者之间是否有区别,但是这里有两种方法,一种使用反射(reflection),另一种使用不安全包(unsafe package)。

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)
}

请注意,这只是代码的翻译部分,不包括其他内容。

英文:

I'm not exactly sure if there's a difference between the two, but here's
two ways, one using reflection and another using the unsafe package.

package main 
import (
&quot;fmt&quot;
&quot;reflect&quot;
&quot;unsafe&quot;
)
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(&quot;reflect.Sizeof(T) = %d, unsafe.Sizeof(T{} = %d)&quot;, s1.Size(), s2)
}

答案3

得分: 0

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

package util

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

var Size []uint64

// Sizeof函数将以字节为单位查找对象的近似大小
func Sizeof(i interface{}) (size uint64) {
	size = 0
	sizeof(reflect.ValueOf(i), &size)
	return
}

// sizeof是用于计算对象大小的私有函数
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:
		/*
			然后遍历数组并递归调用size方法。
			如果所有元素都持有相同的值,则计算一个元素的大小并乘以数组的长度。
			如果数组包含切片或任何动态元素,则不会正确工作。
		*/
		for i := 0; i < val.Len(); i++ {
			sizeof(val.Index(i), sizeptr)
		}

	case reflect.Interface:
		/*
			首先,我们需要使用Elem()方法获取接口的底层对象。
			然后我们需要递归调用此函数。
		*/
		temp := val.Elem()
		sizeof(temp, sizeptr)

	case reflect.Map:
		for _, key := range val.MapKeys() {
			/*
				通过调用size函数获取键的大小
			*/
			sizeof(key, sizeptr)

			/*
				获取由键指向的值,然后递归计算其大小
			*/
			mapVal := val.MapIndex(key)
			sizeof(mapVal, sizeptr)
		}

	case reflect.Ptr:
		prtVal := val.Elem()
		/*
			如果指针无效或指针为nil,则返回而不更新大小
		*/
		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:
		/*
			这里没有考虑NumMethod。不认为这是必要的,或者是吗?
			需要验证这一点...
		*/
		for i := 0; i < val.NumField(); i++ {
			sizeof(val.Field(i), sizeptr)
		}

	case reflect.UnsafePointer:
		/*
			这允许在元素之间进行转换。不认为这在计算大小时使用
		*/
	case reflect.Func:
		// 如何处理函数指针
	case reflect.Chan:
		// 不认为需要处理此情况,因为它将被生成和消耗
	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))

}
英文:

This may be a bit late but may be of help. The Sizeof() would return the size.

package util
import (
&quot;math/cmplx&quot;
&quot;reflect&quot;
&quot;unsafe&quot;
)
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), &amp;size)
return
}
//sizeof  private function which used to calculate size of object
func sizeof(val reflect.Value, sizeptr *uint64) {
if val.Kind() &gt;= reflect.Bool &amp;&amp; val.Kind() &lt;= 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 &lt; 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 &lt; val.Len(); i++ {
sizeof(val.Index(i), sizeptr)
}
case reflect.Struct:
/*
Didn&#39;t consider the NumMethod here. Don&#39;t this that this is necessary or is it??
Need to verify this...
*/
for i := 0; i &lt; 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&#39;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(&quot;0&quot;)
Size[reflect.String] = uint64(unsafe.Sizeof(string_val))
}

huangapple
  • 本文由 发表于 2014年9月30日 15:34:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/26115215.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定