面试题答案
一键面试小对象(小于等于 32KB)
- 分配策略:
- Go 的内存分配器使用 mcache 来管理小对象的分配。每个工作线程都有一个对应的 mcache,它是线程本地缓存。mcache 中维护了一系列的 freelist,每个 freelist 对应一种特定大小的对象。当需要分配小对象时,直接从 mcache 对应的 freelist 中获取空闲内存块。如果 mcache 中没有合适的空闲块,会从 mcentral 中获取一批内存块填充到 mcache 中。
- mcentral 是所有工作线程共享的,它管理着给定大小类别的内存块。它从 mheap 中获取内存,并向 mcache 提供内存块。
- 对性能的影响:
- 优点:由于小对象分配在 mcache 本地进行,避免了多线程竞争,极大地提高了分配效率。减少了锁的竞争,使得小对象的分配和释放操作可以在本地快速完成,提升了程序的整体性能。
- 缺点:因为 mcache 会预先分配一些内存空间,可能会造成一定程度的内存浪费,尤其是在对象大小类别较多时,每个 freelist 都需要预留一定空间。
中对象(大于 32KB 且小于等于 16MB)
- 分配策略:
- 中对象同样使用 mheap 来分配内存。但与小对象不同,中对象不会经过 mcache,而是直接从 mheap 分配。mheap 采用分级的内存管理策略,将内存划分成不同大小的页(page)。对于中对象,会根据其大小找到合适的页来分配,这些页可能是多个连续的物理页。
- 分配时会在 mheap 的空闲列表中查找满足对象大小需求的页,并将该页分配出去。如果没有合适大小的空闲页,mheap 会尝试从操作系统申请新的内存页。
- 对性能的影响:
- 优点:由于中对象不经过 mcache,减少了 mcache 管理的开销,对于较大的对象分配相对高效。同时,mheap 的分级管理策略使得内存分配相对有序,有利于内存的有效利用。
- 缺点:由于 mheap 是共享资源,多线程分配中对象时会产生锁竞争,在高并发场景下可能会影响分配性能。而且如果频繁分配和释放中对象,可能导致 mheap 中内存碎片化,影响后续的内存分配效率。
大对象(大于 16MB)
- 分配策略:
- 大对象直接从操作系统申请内存,绕过了 mcache 和 mheap 的常规分配流程。Go 运行时会调用系统的内存分配函数(如在 Linux 上调用
mmap
)来为大对象分配一块连续的内存空间。 - 当大对象不再使用时,Go 运行时会直接调用系统的内存释放函数(如在 Linux 上调用
munmap
)将内存归还给操作系统。
- 大对象直接从操作系统申请内存,绕过了 mcache 和 mheap 的常规分配流程。Go 运行时会调用系统的内存分配函数(如在 Linux 上调用
- 对性能的影响:
- 优点:直接从操作系统分配内存可以确保大对象获得足够大的连续内存空间,满足大对象对内存的需求。避免了在 Go 运行时内部复杂的内存管理结构中分配大对象可能导致的内存碎片化问题。
- 缺点:每次分配和释放大对象都需要系统调用,系统调用开销较大,相比小对象和中对象在运行时内部的分配方式,大对象的分配和释放性能相对较低。同时,频繁分配和释放大对象可能对操作系统的内存管理造成压力。