垃圾回收机制
垃圾回收是程序语言自行管理内存的一种机制,它的主要原理是检测哪些内存块已经不再被使用的情况,然后回收这些内存块,以供后续程序使用.
GC和手动释放内存的比较
GC的优势在于不需要程序员在编写程序时关心内存分配问题,只需专注于程序的编写即可,而手动释放内存则相对麻烦,首先需要程序员在编写程序时明白各个内存块如何被分配和使用(这就需要程序员有较好的内存管理意识),此外在程序结束时,程序员也需要手动释放内存,这无疑会额外增加程序员的工作量.
GC劣势在于,它无法做到完全按照我们预期的方式来处理内存回收工作,如果没有妥善的规划内存的使用方式,GC可能会导致程序内存浪费严重等问题,而手动释放内存则不会.
在实际工程中,使用手动内存管理时需要程序员有较好的内存管理意识,当程序员没有掌握良好的内存管理意识时,他们往往会产生不必要的内存浪费等问题.而GC则能较好地自动完成这些工作,它无需程序员关注内存分配问题,但可能导致程序内存浪费等问题.
总而言之,手动管理内存要求程序员有一定的记忆分配机制,而GC要求程序员有一定的程序设计能力.
Golang中的内存管理
并发标记清除算法
在Golang中,它使用一种名为并发标记清除的垃圾回收算法来进行内存的管理.它首先扫描根对象,然后并发地扫描所有已找到的对象,直到没有未扫描的对象,然后才对已找到的对象进行回收.
三色标记清除
在Golang中,它使用三色标记清除算法对内存进行回收工作.该算法通过扫描所有根对象和对象引用来跟踪对象的使用状态,并在不需要的对象上运行清除过程.
在Golang中,它首先设置内存中的所有对象为白色,表示这些对象尚未处理过.然后在程序执行的过程中,它会追踪这些对象的引用关系,并在处理对象时将其设置为黑色,表示这些对象已被处理过,不会在扫描过程中再次被处理.如果程序在执行过程中再次遇到已被处理过的对象,则表示该对象不会被再次处理,可以安全删除.
在Golang中,GC的工作分为以下几个步骤:
- 扫描所有根对象
- 并发地扫描所有已找到的对象引用
- 当扫描完成后,程序将查找未被处理的白色对象并删除它们
STW(Stop The World,暂停程序业务逻辑)
在Golang中,当它需要执行GC回收工作时,它会暂停当前任务所在线程,然后在GC中完成程序内存回收工作.此时,如果程序的其他线程在等待当前任务所在的线程返回时,那么它们将等待当前任务所在的线程等待GC回收任务完成.
三色不变式
在Golang中,使用**三色不变式(color invariants)**来确保在程序执行过程中不会发生错误.它要求程序在执行过程中,主要目的是确保一个对象所拥有的颜色始终保持不变.在Golang中,它分为三种三色不变式:强三色不变式,弱三色不变式和混合三色不变式.
强三色不变式
在Golang中,使用**强三色不变式(strong color invariants)**来保证在GC执行过程中不会发生错误.它要求对象在分配时不能为白色,只有在扫描过程中被处理过才能被设置为白色,否则程序将会发生运行错误.
弱三色不变式
在Golang中,使用**弱三色不变式(weak color invariants)**来保证在GC执行过程中不会发生错误.它要求对象在分配时可以是白色,在执行过程中被处理过也可以被设置为黑色,但在执行过程中其他已处理过的对象不会变为白色.
混合三色不变式
在Golang中,使用**混合三色不变式(mixed color invariants)**来保证在GC执行过程中不会发生错误.它要求对象在执行过程中被处理过之后,其颜色与它所引用的对象的颜色相同.
混合写屏障技术
写屏障
在Golang中,它使用**写屏障(write barrier)**技术来支持内存的并发管理.当程序执行过程中分配新的内存时,它会调用一个名为mallocgc的函数,它首先检查分配的内存大小是否超过可配置的最大值.如果没有超过,那么它不会调用写屏障,否则它会进行调用.
混合写屏障技术
在Golang中,为了减少STW事件,使用混合写屏障技术来支持GC功能,当程序分配内存时,首先检查分配的内存大小是否超过可配置的最大值,如果没有超过,那么它将调用malloc函数分配内存块,并且不会调用写屏障,否则它会调用writebarrier函数执行写屏障操作.如果在执行过程中有分配新的内存,那么在写入时,它将调用gcDrain函数来进行写屏障操作,并将分配的内存地址放入名为gchelperptr的变量中.然后在适当的时间点运行辅助GC线程,它将检查是否有未处理的内存对象,如果存在未处理的内存对象,那么它将将这些对象添加到gchelperptr中.然后它会并发地扫描gchelperptr中已找到的对象引用,并将这些对象添加到gchelperptr中.在这个过程中,它还会并发地执行这些任务,直到没有新的内存对象需要处理,然后程序将再次检查是否有未处理的内存对象.
混合写屏障技术具体操作如下:
- 当在程序执行过程中需要分配新内存时,它将调用mallocgc函数分配内存块,然后使用指针将内存块的地址存储到栈中
- 当在编写程序时需要对分配的内存进行赋值操作时,它将调用gcDrain函数来检查内存块,并将分配的内存块放入名为gchelperptr变量中
- 在适当的时间点,程序会运行一个名为gchelper的辅助GC线程来执行GC任务,它首先检查gchelperptr变量是否存在需要处理的内存对象,如果存在,那么它将将其添加到gchelperptr变量中
- 然后它会并发地扫描gchelperptr中的已找到的对象引用,并将这些对象添加到gchelperptr中
- 当没有新的对象需要处理时,该辅助GC线程将再次检查gchelperptr,并从中检查是否存在未处理的内存对象
辅助GC线程
Golang会在适当的时间点运行辅助GC线程,首先检查是否存在未处理的内存对象,如果存在未处理的内存对象,那么它将将这些对象添加到gchelperptr中.然后它会扫描gchelperptr中已找到的对象引用,并将这些对象添加到gchelperptr中.在这个过程中,它还会并发地执行这些任务,直到没有新的内存对象需要处理,然后程序将再次检查是否有未处理的内存对象.
结束
本篇文章主要是对程序的内存分配、回收和编程语言的内存管理方式的一个了解,并具体地通过Golang来了解GC的算法.总的来说,本文算是对Golang的GC的算法有一个大致的印象,更深入的东西还有待于之后的深入学习.