内存管理
堆内存使用
- 会将使用的内存形成一个链表,内存使用结束以后,会标记成unuse,垃圾回收就会扫描整个链表,进行清理
- 内存管理可能存在的问题
- 频繁内存分配的收,系统性能降低
- 多线程共享内存空间,同时申请内存需要加锁,否则会产生多个线程访问同一个内存的问题
- 内存碎片的问题,经过不断的内存分配回收,内存变得不连续
- thteadcachemalloc(tcMalloc)解决上述问题
- 每个线程有单独的内存空间,从而解决多线程内存申请需要加锁的问题(threadcache)
- threadCache中没有空间以后,会从CentralCache中获取,在centralCache中获取内存需要加锁
- 然后是pageHeap,pageheap里是一个个Span list,span list是一个个的8k大小的内存页
- sizeclass,空间规格,每个span都带有一个sizeclass,标记每个span如何使用
go内存分配
- 基于tcmalloc,span class 134个,size class67个,sapn class一个带指针,一个不带,便于回收
- free树,未进行分配的内存树
- mcav树,会瘦回来的内存树
内存回收
- 引用计数(php、py、swift),对每个对象维护一个引用计数,销毁时回收,不好处理循环引用
- 标记删除(go),从根变量开始遍历所有的引用对象,引用的标记为引用,没被标记的进行回收,缺点是需要暂停程序回收时
- 分代回收,java
mspan
- mspan记录了起始地址等
- allocBits,bitmap记录了每块内存的分配情况,被分配的标记成1
- gcmarkBits,记录了每块内存的引用情况,有对象引用的标记为1,没有的为0,通过这个两个bitmap,可以实现快速的内存回收
gc流程
- 大部分处理是和用户代码并行的
- Mark
- Mark Prepare:初始化gc任务,包括开启写屏障和辅助GC,统计root的任务数量等,整个过程需要STW(程序停止)
- GC Drains,扫描所有root对象,包括全局指针和goroutine栈上的指针(扫描哪个停止哪个协程),将其加入标记队列,并循环处理灰色队列的对象,直到灰色队列为空
- Mark Termination:完成标记工作,重新扫描全局指针和栈,mark阶段有一部分工作是和协程并行的,可能会有新的空余内存产生,通过写屏障记录,然后重新扫描下,这个过程会stw
- sweep:按照标记结果回收白色对象,后台并行
- sweep Termination:对为清扫的span进行清扫,只有上一轮的GC清扫完成才可以开始
三色标记法
- gc开始时,都是白色
- 将扫描到未判定的标记为灰色(在队列中,等待判定的)
- 遍历所有的灰色,将扫到的下一级置为灰色,自身为黑色
- 直到没有灰色的,黑色就为非垃圾,白色为垃圾
垃圾回收实际
- 内存分配量达到阈值gc
- 定期触发,默认2分钟,在src/runtime/proc.go forcegcperiod声明
- 手动触发 程序中使用runtiome.GC() 手动触发
网友评论