深入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()
相关推荐
yourkin6662 小时前
为什么现在 Spring Boot 默认使用 CGLIB 了?
java·开发语言·jvm
胖头鱼不吃鱼-3 小时前
Go 原理之 GMP 并发调度模型
java·jvm·golang
典孝赢麻崩乐急5 小时前
Java学习-----JVM的垃圾回收算法
java·jvm·学习
码字的字节9 小时前
深入理解Java内存与运行时机制:从对象内存布局到指针压缩
java·jvm·内存布局·指针压缩
练习时长两年半的程序员小胡10 小时前
JVM 垃圾回收机制全景解析:从对象回收到收集算法
java·jvm·算法·垃圾回收
笑衬人心。10 小时前
JVM 笔记:类加载、内存管理、垃圾收集与垃圾收集器
java·jvm·笔记
2501_9200470313 小时前
python-内存管理
开发语言·jvm·python
xzkyd outpaper19 小时前
JVM、Dalvik、ART区别
jvm·dalvik
笠码1 天前
JVM Java虚拟机
java·开发语言·jvm·垃圾回收
试着1 天前
零基础学习性能测试第五章:JVM性能分析与调优-垃圾回收器的分类与回收
jvm·学习·零基础·性能测试·垃圾回收器