概念定义
程序以不可预测的尺寸与生命周期要内存,内核按大块给页。分配器是中间层,它的数据结构——尺寸类、空闲链表、arena——为程序创建的每个对象定下成本曲线。
工作机制
小请求映射到尺寸类并走每线程缓存获得无锁快路径;大请求直接 mmap。arena 把堆分片以约束锁争用,代价是 arena 间碎片。slab 分配器按固定形状雕刻并把空闲链表嵌在对象里。加固模式为可利用性防护加守卫页、隔离区与随机化。
使用场景
高线程数且分配密集的服务、碎片会累积的长寿命进程、解析不可信输入的安全敏感路径。切换前后要测 RSS 增长对存活字节的比值、以及分配延迟分布。
常见误区
“malloc 处处一样”不成立:ptmalloc、jemalloc、tcmalloc、mimalloc 在线程缓存、arena 数量与碎片行为上就是设计不同。碎片是“活分配之间的不可用空洞”,泄漏是“无界增长”——不同 bug 不同修法。每线程缓存持有分配统计看不见的内存,线程抖动会推高 RSS;arena 数与 NUMA 拓扑错配会加跨节点流量。换分配器会改变内存布局并唤醒潜伏 bug——运气好的双重释放变崩溃,那是伪装成事故的诊断价值。
代码示例
import os
from typing import BinaryIO, Iterator, Tuple
class ChunkedReadError(ValueError):
"""Raised when chunk parameters are invalid."""
def iter_chunks(path: str, chunk_size: int = 65536, offset: int = 0) -> Iterator[bytes]:
"""Yield the file at path in fixed-size byte chunks starting at offset."""
if chunk_size < 1:
raise ChunkedReadError("chunk_size must be >= 1")
if offset < 0:
raise ChunkedReadError("offset must be >= 0")
if not os.path.isfile(path):
raise FileNotFoundError(f"file not found: {path}")
if offset > os.path.getsize(path):
raise ChunkedReadError("offset is beyond the end of the file")
with open(path, "rb") as handle:
handle.seek(offset)
while True:
chunk = handle.read(chunk_size)
if not chunk:
return
yield chunk
def read_all_chunks(path: str, chunk_size: int = 65536) -> Tuple[bytes, int]:
"""Return the whole file content and the total number of bytes read."""
parts = []
total = 0
for chunk in iter_chunks(path, chunk_size=chunk_size):
parts.append(chunk)
total += len(chunk)
return b"".join(parts), total
export class CircularBuffer<T> {
private readonly items: Array<T | undefined>;
private head = 0;
private count = 0;
public constructor(public readonly capacity: number) {
if (!Number.isInteger(capacity) || capacity < 1) {
throw new RangeError("capacity must be a positive integer");
}
this.items = new Array<T | undefined>(capacity);
}
public get size(): number {
return this.count;
}
public get full(): boolean {
return this.count === this.capacity;
}
public push(value: T): T | undefined {
const evicted =
this.count === this.capacity ? this.items[this.head] : undefined;
this.items[this.head] = value;
this.head = (this.head + 1) % this.capacity;
if (this.count < this.capacity) this.count += 1;
return evicted;
}
public at(index: number): T {
if (!Number.isInteger(index) || index < 0 || index >= this.count) {
throw new RangeError(`index out of range: ${index}`);
}
const start =
(this.head - this.count + this.capacity) % this.capacity;
return this.items[(start + index) % this.capacity] as T;
}
public toArray(): T[] {
const output: T[] = [];
for (let index = 0; index < this.count; index += 1) {
output.push(this.at(index));
}
return output;
}
public clear(): void {
this.items.fill(undefined);
this.head = 0;
this.count = 0;
}
public *[Symbol.iterator](): IterableIterator<T> {
for (let index = 0; index < this.count; index += 1) {
yield this.at(index);
}
}
}
const buffer = new CircularBuffer<number>(3);
console.log(buffer.push(1), buffer.push(2), buffer.push(3));
console.log(buffer.push(4));
console.log(buffer.toArray(), buffer.at(0), buffer.at(2), buffer.full);
console.log([...buffer]);
buffer.clear();
console.log(buffer.size, buffer.toArray());
try {
buffer.at(0);
} catch (error) {
console.log(error instanceof Error ? error.message : String(error));
}