深入JVM底层-内存分配算法

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()
相关推荐
微风粼粼8 小时前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
掘金-我是哪吒11 小时前
分布式微服务系统架构第158集:JavaPlus技术文档平台日更-JVM基础知识
jvm·分布式·微服务·架构·系统架构
weixin_ab1 天前
JMM--数据原子操作
jvm
超级小忍1 天前
JVM 中的垃圾回收算法及垃圾回收器详解
java·jvm
喝可乐的布偶猫1 天前
Java类变量(静态变量)
java·开发语言·jvm
abigalexy1 天前
深入JVM底层-垃圾回收GC算法
jvm
麦兜*2 天前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
真实的菜3 天前
JVM类加载系统详解:深入理解Java类的生命周期
java·开发语言·jvm
在未来等你3 天前
JVM调优实战 Day 15:云原生环境下的JVM配置
java·jvm·性能优化·虚拟机·调优