引用计数
Perl采用引用计数的方式来管理内存:每次引用内存数据,该数据的引用计数加1,当引用计数减为0,Perl将回收该内存数据。
具体来说,Perl会对每个内存数据都会维护一个引用计数器:
- 最初创建数据对象时,赋值给初始化变量或者保存在数组或hash中时,该数据的引用数为1
- 以后每次引用、赋值引用、拷贝引用等操作都会对引用数加1
- 将变量赋值为undef,或者,将数组或hash中原本指向该数据的元素设置为undef,将显式取消引用关系,引用数减1
- 引用可能在作用域中,当离开对应作用域时,该作用域内的引用将取消(比如my声明的变量离开作用域时,引用数将1)
- 只要某内存数据的引用计数还未减少为0,该内存数据所占用的内存就不会释放。当引用计数为0时,perl将回收这段内存空间,但不会交还给操作系统,在必要的时候perl会重用这段内存空间存储新数据,而无需再向操作系统申请新内存
例如:
my $name = "junma"; # 数据junma的引用数1
my @arr = (\$name, 23); # 数据junma的引用数2
my $name_ref = \$name; # 数据junma的引用数3
{
my $name_ref1 = $name_ref; # 数据junma的引用数4
} # 数据junma的引用数3,出作用域
undef @arr=undef; # 数据junma的引用数2
$name_ref = 111; # 数据junma的引用数1
$name = undef; # 数据junma的引用数0,junma被perl回收
使用引用计数方式管理内存,它最大的优点在于:
- 即刻回收:只要数据对象的引用计数器为0了,就会立刻被回收,不会推迟
- 暂停时长很短:因为回收时无需遍历内存,所以回收率很高
但使用引用计数方式管理内存也有很大的缺点:
- 要频繁增、减计数,增、减计数的工作压力非常大
- 无法回收循环引用
想象一下,在循环100W次的循环中使用一个字面量,那么增、减该数据的引用计数并回收该数据各100W次。好在,Perl已经对此做好了优化:在循环中会缓存字面量并在循环过程中一直使用该缓存结果。
say \"junma";
say \"junma";
for (1..5){
say \"junma";
}
输出结果:
SCALAR(0x55e14a34dc10) # 不在循环中,字面量地址不同
SCALAR(0x55e14a34dcd0)
SCALAR(0x55e14a7292b0) # 循环过程中字面量地址相同
SCALAR(0x55e14a7292b0)
SCALAR(0x55e14a7292b0)
SCALAR(0x55e14a7292b0)
SCALAR(0x55e14a7292b0)