您好,登录后才能下订单哦!
本篇内容主要讲解“go内存是怎么分配的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“go内存是怎么分配的”吧!
程序中的数据和变量会被分配到程序的虚拟内存中,内存空间包含两个重要的区域:堆(Heap)和栈(Stack)。函数调用的参数、返回值以及局部变量大都会被分配到栈上,这部分内存会由编译器进行管理。不同的语言使用不同的方法管理堆区内存,c语言中,需要主动申请和释放内存,在java中和go中基本上都交由编译器管理,堆中的对象由内存分配器分配以及垃圾回收器
内存管理一般包含三个不同的组件,分别是Mutator(用户程序)、Allocator(分配器)、Collector(收集器)。当用户程序申请内存时,会通过内存分配器申请新内存,而分配器会负责从堆中初始化相应的内存区域。
内存分配器一般包含两种分配方法
这是一种高效的内存分配方法,但是局限性比较大。只需要在内存中维护一个指向内存特定位置的指针,如果申请内存,分配器只需要检查剩余的空闲内存、返回分配的内存区域并修改指针在内存中的位置。
虽然这种方式拥有较快的执行速度以及角度的实现复杂度,但是无法在内存被释放时重用内存。
通常情况下需要与合适的垃圾回收算法配合使用,例如 标记压缩(Mark-Compact)、复制回收(Copying GC)和分代回收(Generational GC)等算法。它们可以通过拷贝的方式整理存活对象的碎片,定期合并空闲内存,提升分配器的性能
这种方法可以重用已经被释放的内存,它在内部维护一个类似链表的数据结构。当申请内存时,空闲链表会一次遍历空闲的内存块,找到足够大的内存,然后申请新的资源并且修改链表
常见的空闲链表分配器的策略有
在go语言中的内存分配器会基于分配的内存大小选择不同的处理逻辑,运行时根据对象的大下将对象分成微对象、小对象和大对象三种
类别 | 大小 |
---|---|
微对象 | (0,16B) |
小对象 | [16B,32KB] |
大对象 | (32KB,+∞) |
因为大多数情况下对象的大小都在32KB以下,而申请的内存大小影响Go语言运行时分配内存的过程和开销,所以分别处理大对象和小对象有利于提高内存分配器的性能。
内存分配器不仅会区别对待大小不同的对象,还会将内存分成不同的级别分别管理,TCMalloc和Go运行时分配器都会引入Thread Cache(线程缓存)、Central Cache(中心缓存)和Page Heap(页堆)三个组件分级管理内存
线程缓存属于每一个的独立的线程,它能够满足线程上绝大多数的内存分配需求,因为不涉及多线程,所以也不需要使用互斥锁来保护内存,能够减少锁竞争带来的性能损耗。当线程缓存不能满足需求时,运行时会使用中心缓存作为补充解决小对象的内存分配,在遇到32KB以上的对象时,内存分配器会选择页堆直接分配大内存。
这种多层级的内存分配设计与计算机操作系统中的多级缓存有些类似,因为多数的对象都是小对象,我们可以通过线程缓存和中心缓存提供足够的内存空间,发现资源不足时从上一级组件中获取更多的内存资源。
在Go1.11版本之后,使用了稀疏的堆内存空间替代了连续的内存,解决了连续内存带来的限制以及在特殊场景下可能出现的问题。
因为所有的内存最终都是要从操作系统中申请的,所以 Go 语言的运行时构建了操作系统的内存管理抽象层,该抽象层将运行时管理的地址空间分成以下四种状态
状态 | 解释 |
---|---|
None | 内存没有被保存或者映射,是地址空间的默认状态 |
Reserved | 运行时持有该地址空间,但是访问该内存会导致错误 |
Prepared | 内存被保留,一般没有对应的物理内存访问该片内存的行为是未定义的可以快速转到Ready状态 |
Ready | 可以被安全访问 |
到此,相信大家对“go内存是怎么分配的”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。