zoukankan      html  css  js  c++  java
  • unsafe.Sizeof 和 unsafe.Offsetof 的理解

    Alignof返回的对齐数是结构体中最大元素所占的内存数,不超过8,

    参考:https://studygolang.com/articles/21827

    unsafe.Sizeof 和 unsafe.Offsetof 的理解

     package main
     
     import (
         "fmt"
         "unsafe"
         "log"
     )
     
     func main() {
     
         var x struct {
             a bool
             b int16
             c []int
         }
     
         /**
         unsafe.Offsetof 函数的参数必须是一个字段 x.f, 然后返回 f 字段相对于 x 起始地址的偏移量, 包括可能的空洞.
         */
     
         /**
         uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
         指针的运算
         */
         // 和 pb := &x.b 等价
     
         pb := (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))
         *pb = 42
         fmt.Println(x.b) // "42"
         
         //m := x{}
         x.a = true
         x.b = 132
         x.c = []int{1,2,2,3,4}
         log.Println("Sizeof:")
         log.Println(unsafe.Sizeof(x.a))
         log.Println(unsafe.Sizeof(x.b))
         log.Println(unsafe.Sizeof(x.c))
         log.Println(unsafe.Sizeof(x))
         log.Println("Offsetof:")
         log.Println(unsafe.Offsetof(x.a))
         log.Println(unsafe.Offsetof(x.b))
         log.Println(unsafe.Offsetof(x.c))
         log.Println("ttt:")
         type SizeOfE struct {
           A byte  // 1
           C byte  // 1 //调换一下B C的顺序,择Sizeof 整个结构体的大小由24变为了16
           B int64 // 8
        }
        var tt SizeOfE
        log.Println(unsafe.Sizeof(SizeOfE{}))
        log.Println(unsafe.Sizeof(tt.A))
        log.Println(unsafe.Sizeof(tt.B))
        log.Println(unsafe.Sizeof(tt.C))
        log.Println(unsafe.Sizeof(tt))
        log.Println("AlignOf:")
        log.Println(unsafe.Alignof(tt.A))
        log.Println(unsafe.Alignof(tt.B))
        log.Println(unsafe.Alignof(tt.C))
        log.Println(unsafe.Alignof(tt))
    
    }
    

      ------------------------

    https://www.jianshu.com/p/29ac532e7c96

    如何得到一个对象所占内存大小?

    fmt.Println(unsafe.Sizeof(int64(0))) // "8"
    
    type SizeOfA struct {
        A int
    }
    unsafe.Sizeof(SizeOfA{0}) // 8
    
    type SizeOfC struct {
        A byte  // 1字节
        C int32 // 4字节
    }
    unsafe.Sizeof(SizeOfC{0, 0})    // 8
    unsafe.Alignof(SizeOfC{0, 0}) // 4
    结构体中A byte占1字节,C int32占4字节. SizeOfC占8字节
    
    

    内存对齐:

    为何会有内存对齐?1.并不是所有硬件平台都能访问任意地址上的任意数据。2.性能原因 访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存只需访问一次。
    上面代码SizeOfC中元素一共5个字节,而实际结构体占8字节
    是因为这个结构体的对齐倍数Alignof(SizeOfC) = 4.也就是说,结构体占的实际大小必须是4的倍数,也就是8字节。

    type SizeOfD struct {
        A byte
        B [5]int32
    }
    unsafe.Sizeof(SizeOfD{})   // 24
    unsafe.Alignof(SizeOfD{})  // 4
    

    Alignof返回的对齐数是结构体中最大元素所占的内存数,不超过8,如果元素是数组那么取数组类型所占的内存值而不是整个数组的值

    type SizeOfE struct {
        A byte  // 1
        B int64 // 8
        C byte  // 1
    }
    unsafe.Sizeof(SizeOfE{})    // 24
    unsafe.Alignof(SizeOfE{}) // 8
    

    SizeOfE中,元素的大小分别为1,8,1,但是实际结构体占24字节,远超元素实际大小,因为内存对齐原因,最开始分配的8字节中包含了1字节的A,剩余的7字节不足以放下B,又为B分配了8字节,剩余的C独占再分配的8字节。

    type SizeOfE struct {
        A byte  // 1
        C byte  // 1
        B int64 // 8    
    }
    unsafe.Sizeof(SizeOfE{})    // 16
    unsafe.Alignof(SizeOfE{}) // 8
    

    换一种写法,把A,C放到上面,B放到下面。这时SizeOfE占用的内存变为了16字节。因为首先分配的8字节足以放下A和C,省去了8字节的空间。
    上面一个结构体中元素的不同顺序足以导致内存分配的巨大差异。前一种写法产生了很多的内存空洞,导致结构体不够紧凑,造成内存浪费。

    下面我们来看一下结构体中元素的内存布局:

    unsafe.Offsetof:返回结构体中元素所在内存的偏移量

    type SizeOfF struct {
        A byte
        C int16
        B int64
        D int32
    }
    unsafe.Offsetof(SizeOfF{}.A) // 0
    unsafe.Offsetof(SizeOfF{}.C) // 2
    unsafe.Offsetof(SizeOfF{}.B) // 8
    unsafe.Offsetof(SizeOfF{}.D) // 16
    

    下图为内存分布图:


     
    SizeOfF 内存布局图

    蓝色区域是元素实际所占内存,灰色为内存空洞。

    下面总结一下go语言中各种类型所占内存大小(x64环境下):

    X64下1机器字节=8字节


     
    Golang内置类型占用内存大小

    总结一下:

    从例子中可以看出,结构体中元素不同顺序的排列会导致内存分配的极大差异,不好的顺序会产生许多的内存空洞,造成大量内存浪费。
    虽然这几个函数都在unsafe包中,但是他们并不是不安全的。在需要优化内存空间时这几个函数非常有用。



    作者:郭老汉
    链接:https://www.jianshu.com/p/29ac532e7c96
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    查看硬件的一些命令
    InfluxDB部署和使用
    利用mysql时间函数监控表中有没有当天数据
    js基础_for循环(学习笔记)
    JS基础_运算符的优先级
    JS基础_相等运算符
    Apache安装与卸载(win10系统测试)
    MySQL --添加环境变量教程
    MySQL安装教程及遇到的错误提示
    如何完全卸载MySQL
  • 原文地址:https://www.cnblogs.com/oxspirt/p/14667297.html
Copyright © 2011-2022 走看看