一. 内存管理概述
内存管理是编程语言设计中至关重要的一环,它直接影响着程序的性能、稳定性和开发效率。Go 语言作为一门现代化的编程语言,在内存管理方面做了许多精妙的设计,使其在高效、安全和易用性之间取得了良好的平衡。
内存管理一般包含三个不同的组件,分别是用户程序(Mutator)、分配器(Allocator)和收集器(Collector),当用户程序申请内存时,它会通过内存分配器申请新内存,而分配器会负责从堆中初始化相应的内存区域。
在了解内管管理之前,我们先了解下什么是堆内存?什么是栈内存?什么是堆?什么是栈?
二. 堆和栈:程序运行时的内存空间
在计算机科学中,堆(Heap) 和 栈(Stack) 是程序运行时用于存储数据的两种重要内存区域。它们有着不同的特性和用途,理解它们的区别对于编写高效、可靠的程序至关重要。
1、 栈 (Stack)
栈是一种遵循 后进先出 (LIFO) 原则的数据结构,类似于一摞盘子,最后放上去的盘子会被最先拿走。
2、堆 (Heap)
堆是一种用于动态内存分配的内存区域,程序可以在运行时从堆中申请和释放内存。
堆和栈的比较
特性 | 栈 | 堆 |
---|---|---|
分配方式 | 编译器自动分配 | 手动分配 (new/malloc) |
释放方式 | 编译器自动释放 | 手动释放 (delete/free) |
效率 | 高 | 低 |
空间 | 小 | 大 |
线程安全 | 线程私有 | 线程共享 |
生命周期 | 与函数调用绑定 | 灵活 |
三. 内存分区
在 Go 语言中,程序运行时的内存空间主要分为以下几个部分:
四. 栈内存详解
栈内存的特点
栈内存的分配
当一个函数被调用时,编译器会为该函数的局部变量和参数在栈上分配内存空间。函数返回时,这些内存空间会被自动释放。
栈内存的优缺点
优点
缺点
五. 堆内存详解
堆内存的特点
堆内存的分配
堆内存的释放
堆内存的释放由垃圾回收器负责管理。垃圾回收器会定期扫描堆内存,回收不再使用的对象。
堆内存的优缺点
优点:
缺点:
六. 逃逸分析
Go 语言的编译器会进行逃逸分析,确定变量是分配在栈上还是堆上。如果一个变量在函数返回后仍然被引用,则它会被分配到堆上,否则会被分配到栈上。
逃逸分析可以帮助编译器优化代码,减少堆内存的分配,提高程序性能。
七. Go 内存管理组件
Go 语言的内存分配器包含内存管理单元、线程缓存、中心缓存和页堆几个重要组件:
八. 内存管理思想
Go 内存管理核心思想可以分为以下几点:
TCMalloc 是由 Google 开发的一种内存分配器,主要用于优化多线程环境下的内存分配和释放性能。TCMalloc 是Thread-Caching Malloc 的缩写,即线程缓存分配器。
TCMalloc 比 glibc 中的 malloc 还要快很多。Go 的内存分配器就借鉴了 TCMalloc 的设计实现高速的内存分配,它的核心思想是使用多级缓存并将对象根据大小分类,按照类别实施不同的分配策略。
TCMalloc 中将内存分成三类,即小对象,小于256K的,中型对象,介于256K到1M的,大于1M的为大对象。
TCMalloc 不仅会区别对待大小不同的对象,还会将内存分成不同的级别分别管理,分为线程缓存(Thread Cache)、中心缓存(Central Cache)和页堆(Page Heap)。
TCMalloc 的核心思想是:
(1)内存切分,减少碎片。
(2)分级管理,无锁并降低锁的粒度。
(3)回收复用
九. 其他内存分配方法
除了上面讲的TCMalloc内存方法还有其他两种内存分配方法,一种是线性分配器,另一种是空闲链表分配器。
线性分配器
线性分配器(Bump Allocator)是一种高效的内存分配方法,但是有较大的局限性。当我们使用线性分配器时,只需要在内存中维护一个指向内存特定位置的指针,如果用户程序向分配器申请内存,分配器只需要检查剩余的空闲内存、返回分配的内存区域并修改指针在内存中的位置,即移动下图中的指针:
空闲链表分配器
空闲链表分配器(Free-List Allocator)可以重用已经被释放的内存,它在内部会维护一个类似链表的数据结构。当用户程序申请内存时,空闲链表分配器会依次遍历空闲的内存块,找到足够大的内存,然后申请新的资源并修改链表:
评论