Nim 的 ARC/ORC 简介
让我们先了解一下历史:传统上 Nim 是一种使用垃圾回收器(GC)的语言。 大多数标准库都依赖于 GC 来工作。
当然,您可以禁用 GC ,手动管理内存,但无法使用大部分( 很大部分)标准库了。
很长时间里,Nim 默认用的 GC 策略是 refc
(延迟引用计数法:分段标记和扫描循环收集),还有其他几个可选策略:如 markAndSweep
, boehm
, 和 go
可用。
但在过去几年中,Nim 有了新的想法,与析构函数、所有权引用(运行时)等相关:
其中一些想法已经在ARC中实现。
ARC 是什么
ARC 是一个内存管理策略。 它的核心模型基于 析构和移动语义(move)的自动引用计数。很多人错把 Nim 的 ARC 认为是 Swift 的ARC,但他们有很大的区别:Nim 的 ARC 不是原子的引用计数。
引用计数是释放程序中弃用资源的最流行算法之一。任何托管(由运行时控制)引用的引用计数是该引用在其他地方使用的次数。当该计数变为零时,引用及其所有基础数据都将被释放。
引用被其他地方引用的次数,就是引用的计数。当计数变为零时,引用及其所有基础数据都将被释放。
ARC 和 Nim 其他的 GC 最主要的不同是:ARC 是完全确定性的:当编译器认为某个变量(字符串、序列、引用或其他)不再需要,它会自动注入析构函数。
从这个意义上讲,它与C++的析构函数(RAII)相似。
为了说明,我们可以使用 Nim 的 expandArc
内省(将在 Nim 1.4 中提供)。
我们看下面简单的代码:
proc main =
let mystr = stdin.readLine()
case mystr
of "hello":
echo "Nice to meet you!"
of "bye":
echo "Goodbye!"
quit()
else:
discard
main()
然后在 main
程序上生成 Nim 的 ARC IR(中间表示) ,运行 nim c --gc:arc --expandArc:main example.nim
。
var mystr
try:
mystr = readLine(stdin)
case mystr
of "hello":
echo ["Nice to meet you too!"]
of "bye":
echo ["Goodbye!"]
quit(0)
else:
discard
finally:
`=destroy`(mystr)
我们在这里看到的是非常有趣的:Nim 编译器把main
函数包装了在一个try: finally
语句块中(即使 try
块语句内发生了异常, finally
块内的语句也会运行),并将我们的 mystr
(在运行时初始化)的 =destroy
调用插入,以便在不再需要时(在其生命周期结束时)将其销毁。
这显示了 ARC 的主要功能之一:基于作用域的内存管理。作用域是程序中单独的代码区域。基于作用域的内存管理,就是说编译器将自动在作用域内,插入析构函数调用,使变量在作用域结束后析构。
许多 Nim 构造语句引入了新的作用域:过程(proc)、函数(func)、转换函数(converter)、,
方法(method)、block
语句和表达式、 for
和 while
循环等。
ARC 也有被称为 钩子 的预定义函数,用于变量析构/移动/复制时,重写默认编译器行为。当您想要为类型创建自定义语义、或处理指针的低级操作、或调用外部接口(FFI)时,这些功能尤为有用。
ARC 与 Nim 当前的 refc
GC 相比的主要优点是(包括我上面提到的那些):
基于作用域的内存管理(在作用域之后注入析构函数)- 通常减少程序的内存使用并提高性能。
移动语义 - 编译器静态分析的能力,并在可能的情况下将内存复制转换为移动。
共享堆 - 不同的线程可以访问相同的内存,您在线程之间不需要复制变量来传递它们,而是移动它们。 请参阅在线程间隔离和发送数据的 RFC
FFI更简单 (
refc
需要每个外部线程手动设置GC( ARC 没有这个问题),在使用 Nim 为其他语言创建扩展库(.dll
,.so
,Python 扩展),ARC 成为更好选择。)适合硬实时的 要求的程序。
复制省略(游标推断)在许多情况下将复制减少为简单的游标(别名)。
一般来说,使用 ARC 是惊人的一步:可以让程序变的更快、使用内存更少,并且行为可预测。
要为您的程序启用 ARC ,您只需使用 --gc:arc
开关,或将其添加到项目的配置文件(.nims
或 .cfg
)中。
循环的问题
但是等等!我们忘了什么吗?ARC 是引用计数,众所周知,引用计数本身并不处理“循环”。简而言之,循环引用是指一些变量以类似于循环的方式相互依赖。 让我们看一个简单的例子:我们有3个对象(A、B、C),每个对象都引用另一个对象,用一个图更好地显示:
要查找和收集该循环,我们需要一个循环收集器 - 这是运行时的一个特殊部分,用于查找和删除程序中不再需要的循环。
在 Nim 循环收集中,refc
已经完成了循环的标记和清除,但最好使用 ARC 作为基础,使其变得更好。这让我们想到:
ORC - Nim 循环收集器
ORC是Nim基于ARC的全新循环垃圾收集器。它可以被认为是一个全面的GC,因为它包括一个局部跟踪阶段(与大多数其他进行全局跟踪的跟踪GC相反)。 ORC是使用Nim的异步时应该使用的,因为它包含循环需要处理的问题。
ORC 保留了 ARC 的大部分优势
除了确定性(部分)- 默认情况下,ORC 具有自适应阈值,用于采集周期,以及 硬实时(部分),原因相同。
要启用 ORC ,使用 --gc:orc
编译程序,但 ORC 将来会成为 Nim 的默认GC。
我很兴奋!我如何测试它们?
ARC 在Nim 1.2.x 版本中可用,但由于一些错误的修复,最好等待 Nim 1.4 版本(不久就会发布),该版本将提供 ARC 和 ORC 进行广泛测试。 但如果你急于尝试,有一个1.4版本的RC版本可用。
这就是全部!感谢您阅读这篇文章-我希望您喜欢它,并将享受 ARC/ORC 为 Nim 带来的惊人可能性:)
资源/更多信息: