1、 指针碰撞(Bump the Pointer)
用于连续内存分配,适用于堆内存规整的场景(如复制算法后),通过移动指针分配内存。
原理:
维护一个指针:始终指向堆内存中下一个可用的起始地址
顺序分配:每次分配内存时,只需将指针向后移动所需大小的距离
原子操作:通过CAS(Compare-And-Swap)等原子操作保证线程安全
应用: 新生代的Eden区内存分配(Serial、ParNew等收集器);内存布局规整(无内存碎片)的情况下;使用标记-整理算法的老年代(如Serial Old)
优点: 分配速度快(只需移动指针);无内存碎片(连续分配);实现简单
缺点: 要求内存绝对规整(需要垃圾收集器配合);需要处理内存压缩(解决碎片问题);多线程竞争时需要同步
算法图解:


代码示例:
python
class PointerBumpAllocator:
def __init__(self, total_memory):
# 总内存大小
self.total_memory = total_memory
# 模拟内存空间
self.memory = bytearray(total_memory)
# 指向下一个空闲内存位置的指针
self.top = 0
# 记录分配的对象和其内存范围
self.allocations = {}
def allocate(self, size):
"""分配内存"""
if self.top + size > self.total_memory:
raise MemoryError("Out of memory")
start = self.top
end = self.top + size
# 移动指针到下一个空闲位置
self.top = end
# 在实际JVM中这里会初始化对象头等信息
# 这里我们简单记录分配信息
# 用内存起始地址作为对象ID
obj_id = id(start)
self.allocations[obj_id] = (start, end)
return obj_id
def free(self, obj_id):
"""释放内存 - 简单实现中我们只是从记录中移除"""
if obj_id in self.allocations:
del self.allocations[obj_id]
# 实现内存合并
def show_memory(self):
"""可视化内存使用情况"""
print("Memory usage:")
for i in range(0, self.total_memory, 10):
status = []
for j in range(i, min(i + 10, self.total_memory)):
allocated = False
for _, (start, end) in self.allocations.items():
if start <= j < end:
allocated = True
break
status.append('X' if allocated else '.')
print(f"{i:03d}-{i + 9:03d}: {''.join(status)}")
print()
if __name__ == "__main__":
# 创建100字节的内存空间
allocator = PointerBumpAllocator(100)
# 分配一些内存
obj1 = allocator.allocate(20)
obj2 = allocator.allocate(15)
obj3 = allocator.allocate(30)
allocator.show_memory()
# 释放一个对象
allocator.free(obj2)
print("After freeing obj2:")
allocator.show_memory()
# 尝试再分配
obj4 = allocator.allocate(10)
print("After allocating obj4:")
allocator.show_memory()
2、 空闲列表(Free List)
原理:
链表结构:将所有空闲的内存块通过链表形式连接起来
内存分配:当需要分配内存时,遍历链表寻找合适大小的空闲块
内存回收:释放的内存会被重新加入到空闲链表中
策略:
次适应(First-Fit):从链表头开始查找,选择第一个足够大的块
最佳适应(Best-Fit):遍历整个链表,选择最适合(最小足够)的块
最差适应(Worst-Fit):总是选择最大的空闲块
合并: 为了防止内存碎片化,相邻的空闲块会被合并
优点: 实现相对简单,适用于不规则的内存分配模式,可以灵活处理不同大小的内存请求
缺点: 可能导致内存碎片,分配时间不稳定(取决于链表长度),需要额外的空间存储链表指针
应用: CMS收集器:使用空闲列表管理老年代空间;G1收集器:每个Region维护自己的空闲列表;Serial/Parallel收集器:在老年代使用空闲列表
算法图解:


代码示例:
python
class FreeList:
def __init__(self, total_size):
"""
初始化空闲列表
total_size: 总内存大小
"""
self.total_size = total_size
# 初始时整个内存都是空闲的
self.free_blocks = [{'start': 0, 'size': total_size}]
def allocate(self, size):
"""
分配内存块
size: 请求的内存大小
return: 分配的内存起始地址,如果分配失败返回-1
"""
for i, block in enumerate(self.free_blocks):
if block['size'] >= size:
# 找到足够大的空闲块
allocated_start = block['start']
remaining_size = block['size'] - size
# 移除原空闲块
del self.free_blocks[i]
# 如果有剩余空间,添加回空闲列表
if remaining_size > 0:
self.free_blocks.append({
'start': allocated_start + size,
'size': remaining_size
})
# 合并相邻的空闲块
self._coalesce()
return allocated_start
# 没有找到合适的空闲块
return -1
def deallocate(self, start, size):
"""
释放内存块
start: 要释放的内存起始地址
size: 要释放的内存大小
"""
# 将释放的块加入空闲列表
self.free_blocks.append({'start': start, 'size': size})
# 合并相邻的空闲块
self._coalesce()
def _coalesce(self):
"""
合并相邻的空闲块
"""
if not self.free_blocks:
return
# 按起始地址排序
self.free_blocks.sort(key=lambda x: x['start'])
merged = [self.free_blocks[0]]
for current in self.free_blocks[1:]:
last = merged[-1]
if last['start'] + last['size'] == current['start']:
# 相邻块,合并它们
last['size'] += current['size']
else:
merged.append(current)
self.free_blocks = merged
def display(self):
"""显示当前空闲列表状态"""
print("空闲列表:")
for block in sorted(self.free_blocks, key=lambda x: x['start']):
print(f" Start: {block['start']}, Size: {block['size']}")
if __name__ == "__main__":
# 创建一个总大小为1000的空闲列表
free_list = FreeList(1000)
free_list.display()
print("\n分配 200 bytes...")
addr1 = free_list.allocate(200)
print(f"分配 at {addr1}")
free_list.display()
print("\n分配 300 bytes...")
addr2 = free_list.allocate(300)
print(f"分配 at {addr2}")
free_list.display()
print("\n回收 first block...")
free_list.deallocate(addr1, 200)
free_list.display()
print("\n分配 150 bytes...")
addr3 = free_list.allocate(150)
print(f"分配 at {addr3}")
free_list.display()