先实现一个单线程的内存分配.
接下来要实现
- free(包括大对象, 用map实现)
- 多线程缓存 在没有多线程的情况下, 就是线程为1的多线程特例.
- 什么时候将内存还给操作系统? 当一个span被分配之后, 又重新释放到没有对象在使用的时候还给操作系统
- 内存碎片(率)的检测
- 重载 new 和 delete, malloc以及 free new 和 delete 的重载在 test/new_delete/ 中测试. 是对于某个类的这个重载, 而不是全局. 全局的重载会出问题. 因为我的实现依赖了一部分系统的malloc和new. 如果我的全局重载了, 那么会出现递归调用(死递归)
关于多线程缓存.
如果用线程id作为唯一标识, 那么一个线程申请了空间, 然后过了一段时间线程销毁了, 我应该将这个资源还给全局吗?
还是暂时保留, 因为id是会复用的. 但是暂时保留会带来的问题是, 如果那个线程id之后都没有再使用过, 怎么办?
或者第三种方式, 线程销毁的时候, 并不还给全局, 而是加入到一个等待队列中, 等到之后有新的线程的时候, 从这个列表中请求分配资源cache.
最开始的时候, 这个队列为空, 所以需要新建一个. 之后每次线程结束都将这个cache结构加入到等待队列. 其他线程需要申请的时候, 先从这个队列中取, 如果取不到, 那么建立新的. 绑定的时候使用线程id进行绑定.
突然想到一个办法, 那就是我并不需要给每个线程都绑定一个 cache, 给CPU绑定就可以了! 对于一个CPU核心, 在这个CPU上工作的线程是不可能发生并发执行的.
但是我觉得这样会损失局部性. 因为一个线程这次可能在cpu1上运行, 那次在cpu2上运行. 切换之后, 之前在cpu1上的负载到cpu2上就不一样了, 就是另一种分配模式. 会造成一些性能的影响.
cache与线程的生命周期.
tcmalloc 如何用自己的函数替代官方的 malloc, free, new, delete 的?
这跟 符号链接有关, 弱符号, 强符号. 在tcmalloc中, 这部分的定义在 libc_override_gcc_and_weak.h 文件中.
https://wallenwang.com/2018/11/tcmalloc/ : 使用 gcc编译器一节
tcmalloc负责内存分配的对象在main函数执行之前就生成了, 并托管了系统的内存分配的
一个 static 的变量应该是在main执行之前就会执行的了. 所以即使是类, 也可以通过一个这样的变量来调用自己的分配方法从而执行内存分配
如何使用 cmkae生成我的内存分配器的动态库/静态库呢?
我发现我的代码里面用我的malloc替换c标准库里的malloc会出现死循环. 因为我的代码里面会调用 new, 它的实现似乎调用了malloc; 然后这个malloc由于被我替代了, 于是又调用我的malloc, 就这样出现了递归调用, 永远都出不去了.
解决方法: 在我的实现中, 不能调用 new 或者 malloc, 而应该使用自己的mmap分配的内存来进行分配.
但是, 对每一种对象去规划内存是比较复杂, 我可以使用一种较简化的方法, 那就是在使用 new 的对象里重载 new 操作函数. 将new重载成调用mmap获取内存, 而不是让它使用默认的malloc.
但是也比较复杂, 因为对我的每一个动态分配的结构调用了 malloc, 进程中其他部分, 如 stl 的实现也调用了 malloc, 我无法对每一个这种结构重写new函数.
我使用的解决方式是, 不使用 malloc函数, 而简单地使用另一个名字, 如 amalloc.
对内存分配器进行测试, 有一下测试思路
- 单/多线程, 一直 alloc 的测试. 查看内存碎片化率
- 单/多线程, 一边 alloc, 一边 free 的测试
- 不同size资源的测试
- 分配速度的测试
我的分配器目前的问题在于, free了之后不会将空间返还给操作系统.
可以增加一个策略: 当一个span在经过free之后, 已分配的对象为0. 那么他就可以返还给操作系统. (√)
我还发现的一个奇怪的地方就是, 我用malloc或者自己的amalloc分配的内存的大小如果是400+MB, 在Linux上测试得到的只有 200+MB. 为什么反而更少.