소스 검색

feat: 添加引用计数解释

usuifohe 2 년 전
부모
커밋
8bc233d600
1개의 변경된 파일109개의 추가작업 그리고 0개의 파일을 삭제
  1. 109 0
      垃圾回收/README.md

+ 109 - 0
垃圾回收/README.md

@@ -378,6 +378,115 @@ BBoP算法的设计目标是为了在大规模堆内存的情况下提供高效
 
 可以让对象事先记录下有多少个对象引用自己,让没有对象引用的对象自己消失,这就是引用计数
 
+### 算法
+
+需要引入一个**计数器**,计数器记录了被多少个对象引用
+
+> 计数器是无符号整数,用于计数器的位数根据算法和实现而有所不同
+
+在 GC标记-清除算法 等其他GC算法中,没有分块时 mutator 会调用 `garbage_collect` 等函数来启动 GC 分配空闲的内存空间
+
+但是在 引用计数 算法中并没有 mutator 明确启动 GC 的语句
+
+引用计数法与 mutator 的执行密切相关,它在 mutator 的处理过程中通过增减计数器的值来进行内存管理,只有在 `new_obj` 和 `、update_ptr` 函数触发时会发生计数器的值的变化
+
+```cpp
+new_obj(size) {
+    obj = pickup_chunk(size, $free_list)
+
+    if(obj == NULL) {
+        allocation_fail()
+    } else {
+        obj.ref_cnt = 1
+        return obj
+    }
+}
+```
+
+> ref_cnt 表示对象的 **计数器**,对象创建时赋值为1
+
+与标记清除算法类似,生成新对象时会调用 `new_obj` 函数
+
+在 引用计数 法中,除了连接到空闲链表的对象,其他所有对象都是活动对象
+
+```cpp
+update_ptr(ptr, obj) {
+    inc_ref_cnt(obj)
+    dec_ref_cnt(*ptr)
+    *ptr = obj
+}
+
+inc_ref_cnt(obj) {
+    obj.ref_cnt++;
+}
+
+dec_ref_cnt(obj) {
+    obj.ref_cnt --;
+    if(obj.ref_cnt == 0) {
+        for(child: children(obj)) {
+            dec_ref_cnt(*child)
+        }
+        reclaim(obj)
+    }
+}
+```
+
+> mutator 在更新指针时会执行此函数
+
+`update_ptr` 用于更新指针 ptr,使其指向对象 obj,同时进行计数器的增减
+
+1. 对指针 ptr 新引用的对象(obj)的计数器进行增量操作
+2. 对指针 ptr 之前的对象(*ptr)的计数器进行减量操作
+
+减量操作之后,计数器的值为0的对象变成垃圾,因此这个对象的指针会全部被删除,注意 `dec_ref_cnt` 函数递归执行了
+
+至于为什么先执行 `inc_ref_cnt` 再执行 `dec_ref_cnt`, 是为了处理 *ptr 和 obj 是同一对象的情况。 如果反过来的话 obj 可能再增量引用计数之前就被释放了
+
+### 优、缺点
+
+- **优点**
+
+1. 可立即回收垃圾,每个对象始终知道自己的被引用数,当引用数归零式自己就把自己回收了
+2. 最大暂停时间短,只有通过 mutator 更新指针时程序才会执行垃圾回收
+3. 没有必要沿着指针查找,与 标记清除 不同,引用计数 无需由根沿指针查找
+
+- **缺点**
+
+1. 计数器 值的增减处理繁重,大多数情况下指针都会频繁更新,计数器的值都会随之更新,因此值的增减处理必然会变的繁重
+2. 计数器 需要占用很多位
+3. 实现繁琐复杂
+4. 循环引用无法回收
+
+### 延迟引用计数法
+
+### Sticky引用计数法
+
+在 引用计数 中,需要考虑为 计数器 设置多大的位宽
+
+加入为对象设置1个字节的位宽,但是对象本身只有 2个字节的大小,那么平白无故多浪费了0.5倍大的内存空间
+
+那么 Sticky引用计数法 就是通过减少计数器的位宽用来处理这个的问题的
+
+假设计数器的位宽为5位,那么最多只能数到 2的5次方减1,也就是31个引用计数。如果对象被大于31个对象引用,那么计数器就会溢出,针对溢出有2种解决方式
+
+1. 什么都不做
+
+对于计数器溢出的对象,我们可以不再增减其计数器的值,也不销毁这个对象,就让他占用内存空间
+
+因为绝大多数对象一生成马上就死了,也就是说在很多情况下,计数器的值会在0~1的范围内变化,鲜少出现5位计数器益处的情况
+
+另一个方面来说,计数器溢出的对象在执行中的程序里占有非常重要的位置,所以未来成为垃圾的可能性也很低
+
+考虑到上面的两点,对于计数器溢出的对象,什么都不做也不是不行
+
+2. 使用 GC标记-清除算法 进行管理
+
+
+
+### 1位引用计数法
+
+### 部分标记-清除算法
+
 ## GC复制算法
 
 ## GC标记-压缩算法