Skip to content

Latest commit

 

History

History
233 lines (168 loc) · 5.53 KB

File metadata and controls

233 lines (168 loc) · 5.53 KB

1.2 内存管理

📍 导航返回目录 | 上一节:进程管理 | 下一节:文件系统


核心原理

虚拟内存机制

Linux 采用虚拟内存技术,将物理内存抽象为虚拟地址空间:

  • 虚拟地址空间:每个进程拥有独立的 4GB(32位)或 256TB(64位)地址空间
  • 页表(Page Table):虚拟地址到物理地址的映射表
  • TLB(Translation Lookaside Buffer):页表缓存,加速地址转换

进程地址空间布局(64位)

高地址 0x7fffffffffff
├── 内核空间(1TB)
├── 栈(Stack)↓ 向下增长
├── ...
├── 内存映射区(mmap)
├── ...
├── 堆(Heap)↑ 向上增长
├── BSS 段(未初始化全局变量)
├── 数据段(已初始化全局变量)
└── 代码段(Text)
低地址 0x0000000000000000

内存分配机制

  1. 小块内存(< 128KB):通过 brk()/sbrk() 扩展堆
  2. 大块内存(≥ 128KB):通过 mmap() 匿名映射
  3. 内存池技术:减少系统调用,提高分配效率(如 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  # 统计缺页次数

实战示例:内存池实现(Go)

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)
  • 定期重启服务释放碎片

大页内存(Huge Pages)

作用:减少 TLB 缺失,提升性能

适用场景:大内存应用(数据库、缓存)

# 配置大页
echo 1024 > /proc/sys/vm/nr_hugepages

# 查看大页使用情况
cat /proc/meminfo | grep Huge

Swap 优化

建议:生产环境禁用或最小化 swap

# 调整 swappiness 参数(0-100,推荐 10 以下)
sysctl vm.swappiness=10

# 永久配置
echo "vm.swappiness=10" >> /etc/sysctl.conf

本章小结

内存管理是操作系统的核心功能,理解虚拟内存、内存分配、页缓存对于优化后台服务性能至关重要。

关键要点

  • ✅ 虚拟内存提供了地址空间隔离
  • ✅ 理解堆、栈、mmap的使用场景
  • ✅ 内存池可显著减少系统调用开销
  • ✅ 学会使用pprof排查内存泄漏

扩展阅读


💡 思考题

  1. 为什么需要虚拟内存?物理内存不够直接用不行吗?
  2. 堆和栈的区别是什么?什么时候用哪个?
  3. 内存池的原理是什么?为什么能提升性能?

⏮️ 上一节:进程管理 | ⏭️ 下一节:文件系统