Linux 采用虚拟内存技术,将物理内存抽象为虚拟地址空间:
- 虚拟地址空间:每个进程拥有独立的 4GB(32位)或 256TB(64位)地址空间
- 页表(Page Table):虚拟地址到物理地址的映射表
- TLB(Translation Lookaside Buffer):页表缓存,加速地址转换
高地址 0x7fffffffffff
├── 内核空间(1TB)
├── 栈(Stack)↓ 向下增长
├── ...
├── 内存映射区(mmap)
├── ...
├── 堆(Heap)↑ 向上增长
├── BSS 段(未初始化全局变量)
├── 数据段(已初始化全局变量)
└── 代码段(Text)
低地址 0x0000000000000000
- 小块内存(< 128KB):通过
brk()/sbrk()扩展堆 - 大块内存(≥ 128KB):通过
mmap()匿名映射 - 内存池技术:减少系统调用,提高分配效率(如 tcmalloc、jemalloc)
- LRU(Least Recently Used):淘汰最近最少使用的页面
- 二次机会算法:结合访问位和修改位
- 工作集模型:保持进程活跃页面在内存中
- OOM Killer:内存不足时,选择进程杀死以回收内存
- 页面回收:回收页缓存、匿名页面(swap)
- 内存压缩:将多个页面压缩存储
# 内存使用情况
free -h # 查看系统内存使用
vmstat 1 # 监控虚拟内存统计
cat /proc/meminfo # 详细内存信息
# 进程内存分析
pmap -x <pid> # 查看进程内存映射
cat /proc/<pid>/smaps # 详细内存映射统计
valgrind --leak-check=full ./app # 内存泄漏检测
# Swap 管理
swapon -s # 查看 swap 使用
swapoff -a # 关闭 swap
swapon -a # 启用 swap
# 内存性能
perf stat -e page-faults ./app # 统计缺页次数package main
import "sync"
// MemoryPool 简单的内存池实现
type MemoryPool struct {
bufferSize int
pool sync.Pool
}
// NewMemoryPool 创建内存池
func NewMemoryPool(bufferSize int) *MemoryPool {
return &MemoryPool{
bufferSize: bufferSize,
pool: sync.Pool{
New: func() interface{} {
// 分配固定大小的缓冲区
return make([]byte, bufferSize)
},
},
}
}
// Get 从池中获取缓冲区
func (mp *MemoryPool) Get() []byte {
return mp.pool.Get().([]byte)
}
// Put 归还缓冲区到池中
func (mp *MemoryPool) Put(buf []byte) {
if cap(buf) != mp.bufferSize {
return // 大小不匹配,丢弃
}
buf = buf[:mp.bufferSize] // 重置长度
mp.pool.Put(buf)
}
// 使用示例
func main() {
pool := NewMemoryPool(4096)
// 获取缓冲区
buffer := pool.Get()
// 使用缓冲区
copy(buffer, []byte("Hello, Memory Pool!"))
println(string(buffer[:19]))
// 归还缓冲区
pool.Put(buffer)
}Go 程序:
# 1. 开启 pprof
import _ "net/http/pprof"
go func() {
http.ListenAndServe(":6060", nil)
}()
# 2. 采样堆内存
go tool pprof http://localhost:6060/debug/pprof/heap
# 3. 分析
(pprof) top10
(pprof) list <function_name>Python程序:
import tracemalloc
tracemalloc.start()
# ... 运行代码
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)问题:频繁分配释放导致内存碎片
解决方案:
- 使用内存池技术
- 选择合适的内存分配器(jemalloc、tcmalloc)
- 定期重启服务释放碎片
作用:减少 TLB 缺失,提升性能
适用场景:大内存应用(数据库、缓存)
# 配置大页
echo 1024 > /proc/sys/vm/nr_hugepages
# 查看大页使用情况
cat /proc/meminfo | grep Huge建议:生产环境禁用或最小化 swap
# 调整 swappiness 参数(0-100,推荐 10 以下)
sysctl vm.swappiness=10
# 永久配置
echo "vm.swappiness=10" >> /etc/sysctl.conf内存管理是操作系统的核心功能,理解虚拟内存、内存分配、页缓存对于优化后台服务性能至关重要。
关键要点:
- ✅ 虚拟内存提供了地址空间隔离
- ✅ 理解堆、栈、mmap的使用场景
- ✅ 内存池可显著减少系统调用开销
- ✅ 学会使用pprof排查内存泄漏
💡 思考题:
- 为什么需要虚拟内存?物理内存不够直接用不行吗?
- 堆和栈的区别是什么?什么时候用哪个?
- 内存池的原理是什么?为什么能提升性能?