Go plan9 汇编系列文章:

0. 前言

在 Go plan9 汇编系列文章中,介绍了函数和函数栈的调用。这里继续看内存对齐和递归调用方面的内容。

1. 内存对齐

直接上示例:

type temp struct {
a bool
b int16
c []string
} func main() {
var t = temp{a: true, b: 1, c: []string{}}
fmt.Println(unsafe.Sizeof(t))
}

输出:

32

改写 temp 结构体成员变量位置:

type temp struct {
a bool
c []string
b int16
} func main() {
var t = temp{a: true, b: 1, c: []string{}}
fmt.Println(unsafe.Sizeof(t))
}

输出:

40

为什么移动下结构体成员的位置会对结构体在内存中的大小有影响呢?

打印示例中结构体成员变量地址如下:

# 示例 1
func main() {
var t = temp{a: true, b: 1, c: []string{}} fmt.Println(unsafe.Sizeof(t.a), unsafe.Sizeof(t.b), unsafe.Sizeof(t.c))
fmt.Printf("%p %p %p %p\n", &t, &t.a, &t.b, &t.c) fmt.Println(unsafe.Sizeof(t))
} # go run ex10.go
1 2 24
0xc0000a4000 0xc0000a4000 0xc0000a4002 0xc0000a4008
32 # 示例 2
func main() {
var t = temp{a: true, b: 1, c: []string{}} fmt.Println(unsafe.Sizeof(t.a), unsafe.Sizeof(t.c), unsafe.Sizeof(t.b))
fmt.Printf("%p %p %p %p\n", &t, &t.a, &t.c, &t.b) fmt.Println(unsafe.Sizeof(t))
} # go run ex10.go
1 24 2
0xc00006e090 0xc00006e090 0xc00006e098 0xc00006e0b0
40

可以看到,在为结构体分配内存时是要遵循内存对齐的,内存对齐是为了简化寻址,CPU 可一次找到变量的位置。因为内存对齐的存在,这里示例 2 中虽然变量 a 只占 1 个字节,但却独占了 8 个字节,这对于写代码来说是一种内存消耗,应当避免的。

2. 递归

我们看一个递归的示例:

func main() {
println(sum(1000))
} //go:nosplit
func sum(n int) int {
if n > 0 {
return n + sum(n-1)
} else {
return 0
}
}

输出:

# go run ex7.go
# command-line-arguments
main.sum: nosplit stack over 792 byte limit
main.sum<1>
grows 24 bytes, calls main.sum<1>
infinite cycle

这里我们在 sum 函数前加 //go:nosplit 是要声明这个函数是不可栈分裂的函数。意味着当函数栈满的时候,(内存分配器)不会为它开辟新的空间。

Go 为 goroutine 分配的初始栈空间大小为 2K,如果 main 栈加上 nosplit 的 sum 栈超过 2K,将导致爆栈。

//go:nosplit 拿掉,重新执行:

func main() {
println(sum(100000))
} func sum(n int) int {
if n > 0 {
return n + sum(n-1)
} else {
return 0
}
}

输出:

5000050000

那么 sum 是否可以无限递归呢?我们给 sum 一个大数 10000000000000,接着重新执行:

runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200f8398 stack=[0xc0200f8000, 0xc0400f8000]
fatal error: stack overflow

输出 stack overflow,main 协程的栈是从 0xc0200f8000 到 0xc0400f8000,这里递归所用的栈超过了 goroutine 栈的最大限制 1000000000-byte(超过的意思是 main 栈加上 sum 递归调用的栈超过了最大限制),也就是 1G。


Go plan9 汇编:内存对齐和递归的更多相关文章

  1. 【转】C/C++ struct/class/union内存对齐

    原文链接:http://www.cnblogs.com/Miranda-lym/p/5197805.html struct/class/union内存对齐原则有四个: 1).数据成员对齐规则:结构(s ...

  2. c++编程思想(三)--c++中c 续,重点sizeof和内存对齐

    之前理论性的太多,下面就是代码及理论结合了 1.sizeof()是一个独立运算符,并不是函数,可以让我们知道任何变量字节数,可以顺带学一下struct,union,内存对齐 内存对齐:为了机器指令快速 ...

  3. 深入理解c/c++ 内存对齐

    内存对齐,memory alignment.为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐.原因在于,为了访问未对齐的内存,处理器需要作两次内存访问:然而,对齐的内存访问仅需要一 ...

  4. C/C++中struct/union/class内存对齐

    struct/union/class内存对齐原则有四个: 1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储 ...

  5. C/C++内存对齐原则

    C/C++内存对齐 what && why 当用户自定义类型时(struct 或 class),编译器会自动计算该类型占用的字节数. C/C++ 为什么要内存对齐?我道行太浅,摘抄了网 ...

  6. C++内存对齐总结

    大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...

  7. C/C++: C++位域和内存对齐问题

    1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...

  8. C/C++ 知识点1:内存对齐

    预备知识:基本类型占用字节 在32位操作系统和64位操作系统上,基本数据类型分别占多少字节呢? 32位操作系统: char : 1    int :4    short : 2    unsigned ...

  9. Windows+GCC下内存对齐的常见问题

    结构/类对齐的声明方式 gcc和windows对于modifier/attribute的支持其实是差不多的.比如在gcc的例子中,内存对齐要写成: class X { //... } __attrib ...

  10. c++内存对齐

    内存对齐原则: 1.数据成员对齐规则:struct, union的数据成员,第一个数据成员放在offset为0的地方,之后的数据成员的存储起始位置都是放在该数据成员大小的整数倍位置.如在32bit的机 ...

随机推荐

  1. 什么是JS执行上下文?

    我们都知道,JS代码的执行顺序总是与代码先后顺序有所差异,当先抛开异步问题你会发现就算是同步代码,它的执行也与你的预期不一致,比如: function f1() { console.log('听风是风 ...

  2. JavaScript -- 变量 --手稿

  3. windows中新建文件菜单消失的解决办法

    具体解决办法: https://jingyan.baidu.com/article/cbcede07577f4702f40b4dfd.html 右键中新建文本文件 菜单消失: 注册表编辑器: 路径: ...

  4. 记一次springboot整合rabbitMQ的list序列化问题

    问题:平时传一个类的时候都会继承Serializable实现正确传输,这次我把list<Object>直接丢成了message,导致rabbitMQ不能序列化对象.报错 org.sprin ...

  5. 1、SpringMVC简介

    1.1.MVC 概述 MVC:是一种软件架构的思想,将软件按照模型.视图.控制器来划分: M( Model ):模型层,指工程中的 JavaBean ,作用是处理数据: V( View ):视图层,指 ...

  6. 【Vue2】Axios、Async+Await、解构赋值

    Axios入门使用,Async和Await用法,解构赋值语法 <!DOCTYPE html> <html lang="en"> <head> & ...

  7. 【SpringBoot】16 数据访问P4 整合JPA

    DAO面向SpringData操作 Spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问技术, 包括非关系数据库.Map-Reduce 框架.云数据服务等等: 另外也 ...

  8. 【Shiro】07 散列算法 & 凭证配置

    [散列算法概述] 用于生成数据的摘要信息,不可逆算法,用于存储密码或者密文数据. 常见散列算法类型:MD5.SHA 一般进行散列时提供一个"盐",即系统知道的"干扰数据& ...

  9. cloudpickle —— Python分布式序列化的专用模块

    给出cloudpickle的GitHub地址: https://github.com/cloudpipe/cloudpickle =================================== ...

  10. 破局SAP实施难题、降低开发难度,定制化需求怎样快速上线?

    前言 SAP 是全球领先的业务流程管理软件供应商之一,其提供广泛的模块化解决方案和套件,所开发的软件解决方案面向各种规模的企业,帮助客户规划和设计业务流程.分析并高效设计整个价值链,以更好的了解和响应 ...