如何在连续内存块中构建元素可变的链表用于内存池实现?
完全可以实现,有两种常用的实现方案,都能满足「元素连续存储+可变操作+空闲链表管理」的需求:
方案1:原生无依赖实现(推荐入门使用)
核心思路是用不可变struct保证连续存储,通过封装索引句柄和操作方法屏蔽不可变特性,对外提供可变操作能力:
# 不可变Chunk,保证Vector存储时是连续内存布局 struct Chunk{T} next::Int data::T end # Pool结构体,封装底层存储和空闲链表头指针 struct Pool{T} chunks::Vector{Chunk{T}} free_head::Ref{Int} # 用Ref存储可变的空闲链表头 end # 初始化指定类型和大小的内存池 function Pool(T, size::Int) size < 1 && error("Pool size must be at least 1") chunks = Vector{Chunk{T}}(undef, size) # 初始化链表指针,每个chunk的next指向下一个索引 for i in 1:size-1 chunks[i] = Chunk(i+1, zero(T)) end # 最后一个chunk的next指向第一个,形成循环链表 chunks[end] = Chunk(1, zero(T)) return Pool(chunks, Ref(1)) end # 分配空闲chunk,返回索引作为操作句柄 function allocate!(pool::Pool{T}) where T head = pool.free_head[] # 可自行添加内存耗尽的判断逻辑 pool.free_head[] = pool.chunks[head].next return head end # 释放指定句柄的chunk,插回空闲链表头部 function deallocate!(pool::Pool{T}, handle::Int) where T old_head = pool.free_head[] # 仅修改next指针,保留原有data不重置(可根据需求添加重置逻辑) pool.chunks[handle] = Chunk(old_head, pool.chunks[handle].data) pool.free_head[] = handle return nothing end # 读取chunk存储的数据 get_data(pool::Pool{T}, handle::Int) where T = pool.chunks[handle].data # 修改chunk存储的数据 function set_data!(pool::Pool{T}, handle::Int, new_data::T) where T old_chunk = pool.chunks[handle] pool.chunks[handle] = Chunk(old_chunk.next, new_data) return nothing end
该方案的优势:
- 所有Chunk都存储在Vector的连续内存块中,无额外堆分配,cache友好,符合实时场景需求
- 对外仅暴露索引作为句柄,不会出现元素逃逸到数组外的情况
- 封装接口完全屏蔽了Chunk不可变的特性,使用体验和可变元素完全一致
方案2:基于StructArrays的高性能实现
如果需要更高的字段修改性能,避免每次修改都重新构造Chunk实例,可以将Chunk的字段拆分为独立的连续数组,修改时直接操作对应数组的单个元素即可:
using StructArrays struct Pool{T} next::Vector{Int} # 存储所有chunk的next指针,连续内存 data::Vector{T} # 存储所有chunk的数据,连续内存 free_head::Ref{Int} end function Pool(T, size::Int) size < 1 && error("Pool size must be at least 1") next = collect(2:size+1) next[end] = 1 # 形成循环链表 data = fill(zero(T), size) return Pool(next, data, Ref(1)) end function allocate!(pool::Pool{T}) where T head = pool.free_head[] pool.free_head[] = pool.next[head] return head end function deallocate!(pool::Pool{T}, handle::Int) where T old_head = pool.free_head[] pool.next[handle] = old_head pool.free_head[] = handle return nothing end get_data(pool::Pool{T}, handle::Int) where T = pool.data[handle] set_data!(pool::Pool{T}, handle::Int, new_data::T) where T = (pool.data[handle] = new_data)
该方案的优势:
- 两个数组均为连续内存,整体内存布局和连续存储Chunk完全一致
- 修改next指针或data时无需构造新的结构体实例,性能更高,实现逻辑更简洁
内容的提问来源于stack exchange,提问作者BatWannaBe
相关产品推荐
相关产品推荐

