Golang内存管理(一):堆内存管理
堆内存管理基于操作系统的虚拟内存管理,了解虚拟内存有助于深入理解堆内存管理。在程序中变量的存储位置分布于全局数据区、栈区和堆区。栈空间的管理由操作系统和运行时环境协同完成,而堆空间的分配与释放则需程序自行管理。本文主要关注堆内存管理,动态申请堆内存通常需调用系统函数。然而,频繁的系统调用带来性能损耗,为解决此问题,内存池技术成为常用解决方案。内存池预先向操作系统申请大量内存,程序可自行管理,带来显著优势,包括减少内存浪费和提升资源利用效率。Golang采用内存池架构来管理内存。MSpan描述span的数据结构,MHeap描述堆内存结构体,heapArena描述arena结构体。MCentralMCache与Golang协程调度模型中的P绑定,而非线程,优化内存空间使用,确保每个G使用MCache时不需加锁。Go内存分配流程依赖mallocgc函数,将对象分为三类:Tiny对象分配器针对微对象特殊处理,MCache保存Span
Golang 内存模型详解
当存在两个或以上的goroutine同时对一个数据进行读写操作时(非原子操作),我们称这为数据竟态。在竟态条件下,读取的数据可能非一致,例如一个结构体数据,goroutine1正在读v1版的数据;此时goroutine2进行数据更新到v2;导致goroutine1读取到的数据部分是v1版,部分是v2版。Golang中可以通过基于通道(channel)通信、使用同步原语如Mutex、RWMutex等或原子操作(sync/atomic)等方式实现操作顺序化,避免竟态数据问题。Golang内存模型定义了限制条件,当程序遵循这些条件时可以实现数据免竟态,称为“Hanpens Before”规则。我们将重点解释这一规则。理解Hanpens Before规则有助于我们了解哪些情况下数据可见性有保障,哪些情况不确定,从而避免数据不可见问题。但过度依赖这一规则可能导致程序难以理解。Hanpens Before规则描述了两个行为的明确先后关系,通常由基础策略、逻辑依赖或实现原理决定
关于golang的栈、堆和静态存储区
栈(Stack)与堆(Heap)是计算机内存中两种不同的内存分配和管理方式。它们在数据存储与访问方面存在显著差异,且在物理存储介质上的位置也有所不同。堆位于计算机内存的较高地址部分,动态分配,用于存储动态分配的数据,如运行时创建的对象、数组等。堆内存大小受操作系统与应用程序管理机制限制,可动态调整,数据生命周期长,需显式释放,以防内存泄漏。栈位于内存较低地址部分,是一种线性数据结构,用于存储函数调用上下文、局部变量及返回地址。栈大小固定,由编译器或操作系统管理,数据随作用域结束自动释放。静态存储区(Static Storage Area)位于计算机内存的静态数据段或全局数据区,存储全局变量、常量、数组、切片等,数据从程序启动至结束保持不变,具有全局作用域。栈与堆的主要区别在于存储方式、生命周期、访问速度及分配方式。栈适用于短期存储,内存管理由系统自动处理;堆适用于长期存储,需手动管理内存。栈访问速度快,堆访问速度较慢
Golang内存管理之GC
垃圾回收(Garbage Collection,简称GC)是编程语言自动管理内存的机制,能自动释放不再使用的内存对象,避免内存泄漏。GC功能在现代编程语言中普遍存在,其性能与优化程度是语言比较的关键指标之一。Go语言的GC经历了多次优化,从Go V1.3版本的标记-清除算法,到Go V1.5版本的三色并发标记法,再到Go V1.8版本的三色标记法与混合写屏障机制。标记-清除算法通过标记和清除步骤管理内存,但需要暂停程序执行以保证结果正确性。三色标记法通过将对象分为白色、黑色和灰色,避免了长时间暂停程序带来的性能问题,提高了GC效率。在三色标记法中,若不使用暂停程序的策略(STW),标记过程可能导致对象引用关系改变,影响标记结果的准确性。举例说明:假设程序执行过程中,对象3通过指针p被对象2引用,对象2通过指针p指向对象3。在标记过程不暂停程序的情况下,对象4标记为黑色,之后创建新的指针q指向对象3,同时对象2移除指针p,导致对象3被错误地标记为黑色并被GC回收,造成对象丢失