1、基础知识
1、GC中对象的定义
对象是什么?表示的是通过应用程序利用的数据的集合
对象配置在内存空间里。GC根据情况将配置好的对象进行移动或者销毁。因此对象是GC的基本单位
对象由头(header)和域(field)组成
1.1对象头
保存对象本身的信息:
- 对象的大小
- 对象的种类
这两个内容用来确定内存中存储对象的边界
1.2、对象域
对象使用者在对象中可访问的部分称为"域",包含两部分:
- 指针
- 非指针
指针是指向内存空间中某块区域的值。非指针指的是在编程中直接使用值本身。数值、字符、以及真假值都是非指针
2、指针
GC根据对象的指针搜寻其他对象。两点前提:
- 语言处理程序能判别指针和非指针
- 指针默认指向对象的首地址
3、堆
堆指的是执行程序时存放对象的内存空间。当应用程序申请存放对象时,所需的内存空间就会从堆里面分配
GC是管理堆中已经分配的对象的机制
4、评价GC标准
- 吞吐量
- 最大暂停时间
- 堆使用效率
- 访问的局部性
吞吐量:单位时间内的处理能力。mutator(应用程序)
GC吞吐量 = (堆大小) / (A+B+C)
最大暂停时间:在执行GC的时候是StopTheWorld的,这个时间值
堆使用效率:
访问的局部性:具有引用关系的对象之间通常很可能存在连续访问的情况 PC上有4中存储器,分别是寄存器、缓存、内存、辅助存储器。
2、GC 标记 - 清除算法
该算法由标记阶段 + 清除阶段组成。
2.1、标记阶段
mark_sweep()函数
mark_sweep() {
mark_phase()
sweep_phase()
}
mark_phase()函数
mark_phase() {
for(r : $root)
mark(*r)
}
collector会为堆里的所有活动对象打上标记。首先通过根直接引用的对象。然后递归地标记通过指针数组能访问到的对象,这样就可以把所有活动对象都标记上
mark(obj) {
if(obj.mark == FALSE) // 检查实参的obj是否被标记,为了避免重复标记
obj.mark = TRUE // 对象头部进行位操作置位操作
for(child : childern(obj))
mark(*child)
}
标记完成后:
- 深度优先搜索与广度优先搜索
2.2、清除阶段
collector遍历整个堆,回收没有打上标记的对象 sweep_phase() 函数:
sweep_phase(){
sweeping = $heap_start // 从堆首地址开始遍历对象标志位
while(sweeping < $heap_end)
if(sweeping.mark == TRUE) // 对象标志位
sweeping.mark = FALSE // 取消标志位,等待下一次GC时设置该值
else
sweeping.next = $free_list // 对象连接到"空闲链表"的单向链表
$free_list = sweeping
sweeping += sweeping.size
}
2.3、分配
被清除的对象已经连接到空闲链表了,搜索空闲链表并找寻大小合适的分块,就叫分配(new_obj())
new_obj(size) {
chunk = pickup_chunk(size, $free_list)
if(chunk != NULL)
return chunk
else
allocation_fail() // 未找到合适的分块
chunk = pickup_chunk(size, $free_list) 遍历$free_list,寻找>=size的分块,如果它找到和 size 大小 相同的分块,则会直接返回该分块;如果它找到比 size 大的分块,则会将其分割成 size 大 小的分块和去掉 size 后剩余大小的分块,并把剩余的分块返回空闲链表
First -fit、Best -fit、Worst -fit 的不同.
之前我们讲的分配策略叫作 First -fit。因为在 pickup_chunk() 函数中,最初发现大于等于 size 的分块时就会立即返回该分块。
然而,分配策略不止这些。还有遍历空闲链表,返回大于等于 size 的最小分块,这种策略叫 作 Best -fit。
还有一种策略叫作 Worst -fit,即找出空闲链表中最大的分块,将其分割成 mutator 申请的 大小和分割后剩余的大小,目的是将分割后剩余的分块最大化。但因为 Worst -fit 很容易生成大 量小的分块,所以不推荐大家使用此方法。
除去 Worst -fit,剩下的还有 Best -fit 和 First -fit 这两种。当我们使用单纯的空闲链表时, 考虑到分配所需的时间,选择使用 First -fit 更为明智。
2.4、合并
分配策略不同可能会产生大量的小分块,连接连续分块的操作就是"合并",合并是在清除阶段进行的
合并函数:sweep_phase()
sweep_phase(){
sweeping = $heap_start
while(sweeping < $heap_end)
if(sweeping.mark == TRUE)
sweeping.mark = FALSE
else
if(sweeping == $free_list + $free_list.size) // 判断发现的分块和上次分块是否连续
$free_list.size += sweeping.size // 合并分块
else
sweeping.next = $free_list
$free_list = sweeping
sweeping += sweeping.size
}
2.5、优缺点
优点:对象不会移动 缺点:碎片化严重;空闲空间不连续导致分配时遍历链表耗时长;与写时复制技术不兼容