无论谁,进行大规模游戏开发时,都必不可免地要面对几个G的内存数据的管理。一开始我们可能会想到链表,因为链表是最普遍的动态数据群的模型,但是渐渐地我们发现,对于游戏这样的大规模使用内存的软件,由于多次分配,释放堆内存而导致的内存碎片的影响是极其可怕的,更何况申请堆内存要耗费的时钟周期非常多,远没法满足大型游戏对速度的需求,所以池数据模型应运而生。本文章所讲述的,便是在笔者对池数据模式仅有一个大概认识的情况下提出的思路,被称为 可扩展侵入式索引链表池 简称 索引池 的概念,大神莫喷。
首先为什么会存在内存碎片,为什么内存申请的速度会很慢呢?
内存申请,如new和malloc所进行的工作就是在巨大的堆内存空间中取出一块供使用。而内存碎片是乱顺序的申请,释放所造成的内存间隙,占用空间,却不够大不能再次分配给用户。而至于为什么new和malloc会很慢,那是因为这两个函数要对属于操作系统的内存空间进行操作,所以应该在权限更高的内核程序段中实现,从用户模式向内核模式跳转再跳回来,这就耗费了非常多的时间。
那么,索引池是如何规避这两个问题的呢?
索引池适用于保存数量很多个或可能很多个相同大小的元素。
索引池初始化时会预先申请一块差不多大的内存,足够装下数个相同元素,用元素相对于池内存首地址的元素个数偏移量代替指针,把元素全部链入被称为资源链表的索引式链表中。
当程序向池申请空间,索引池只要将资源链表中的第一个节点取下来供使用即可。
当程序向池交还空间,索引池重新将元素链入资源链表的第一位。
索引池要求每个元素都在结构体最开始的地方保存一个索引值,一个指向下一个元素的索引值,这样一来,池中每个元素本身就是一个链表节点,方便实现索引链表。
索引池的一些做法带来了强大的优化效果,首先池中元素全都等大,所以在一个池中,内存碎片的出现几率是0,所以达到了池内存100%利用。
其次,索引池的链表使用索引,这样一来,索引变量的大小可定,比起64位下8字节指针更节省空间。而且索引与内存地址并无直接关联,当池中已经申请的内存不够大时,只需要再申请一块内存,在第二块内存中接续这第一块内存给元素编号,这样一来,已经分配出去的元素不会因为池内存的改变而出现任何变动。
再次就是索引池每次的分配,回收内存的过程,根本不经历内核转换,而且由于每次都只操作首节点,所以分配耗时只有几个数字运算的时间,几乎可以忽略,将速度发挥到极致。
首先为什么会存在内存碎片,为什么内存申请的速度会很慢呢?
内存申请,如new和malloc所进行的工作就是在巨大的堆内存空间中取出一块供使用。而内存碎片是乱顺序的申请,释放所造成的内存间隙,占用空间,却不够大不能再次分配给用户。而至于为什么new和malloc会很慢,那是因为这两个函数要对属于操作系统的内存空间进行操作,所以应该在权限更高的内核程序段中实现,从用户模式向内核模式跳转再跳回来,这就耗费了非常多的时间。
那么,索引池是如何规避这两个问题的呢?
索引池适用于保存数量很多个或可能很多个相同大小的元素。
索引池初始化时会预先申请一块差不多大的内存,足够装下数个相同元素,用元素相对于池内存首地址的元素个数偏移量代替指针,把元素全部链入被称为资源链表的索引式链表中。
当程序向池申请空间,索引池只要将资源链表中的第一个节点取下来供使用即可。
当程序向池交还空间,索引池重新将元素链入资源链表的第一位。
索引池要求每个元素都在结构体最开始的地方保存一个索引值,一个指向下一个元素的索引值,这样一来,池中每个元素本身就是一个链表节点,方便实现索引链表。
索引池的一些做法带来了强大的优化效果,首先池中元素全都等大,所以在一个池中,内存碎片的出现几率是0,所以达到了池内存100%利用。
其次,索引池的链表使用索引,这样一来,索引变量的大小可定,比起64位下8字节指针更节省空间。而且索引与内存地址并无直接关联,当池中已经申请的内存不够大时,只需要再申请一块内存,在第二块内存中接续这第一块内存给元素编号,这样一来,已经分配出去的元素不会因为池内存的改变而出现任何变动。
再次就是索引池每次的分配,回收内存的过程,根本不经历内核转换,而且由于每次都只操作首节点,所以分配耗时只有几个数字运算的时间,几乎可以忽略,将速度发挥到极致。