R如何引用未分配的值?

时间:2016-10-24 11:32:39

标签: r memory-management

我熟悉显示指定变量的十六进制内存地址的tracemem(),例如

x <- 2
tracemem(x)
#> [1] "<0x876df68>"

但是,当值实际上只是一个未分配的值时,这涉及到什么(引擎盖下)? e.g。

tracemem(4)
#> [1] "<0x9bd93b8>"

同样的问题仅适用于评估没有赋值的表达式

4
#> [1] 4

似乎如果我在控制台中多次评估,我会得到不断增加的十六进制地址

tracemem(4)
#> [1] "<0x8779968>"
tracemem(4)
#> [1] "<0x87799c8>"
tracemem(4)
#> [1] "<0x8779a28>"

但如果我显式循环此操作

for ( i in 1:3 ) { print(tracemem(4)) }
#> [1] "<0x28bda48>"
#> [1] "<0x28bda48>"
#> [1] "<0x28bda48>"

sapply通过replicate

replicate(3, tracemem(4))
#> [1] "<0xba88208>" "<0xba88208>" "<0xba88208>"

即使我明确延迟迭代之间的打印,我也会重复使用相同的地址

for ( i in 1:3 ) { print(tracemem(4)); Sys.sleep(1) }
#> [1] "<0xa3c4058>"
#> [1] "<0xa3c4058>"
#> [1] "<0xa3c4058>"

我最好的猜测是,该来电是指parent.frame eval.parent(substitute( replicate.Primitive中已经临时指定的值,但我对底层{ {1}}代码for以了解它是否在那里做同样的事情。

我有信心R正在创建临时变量,因为我可以做到

list(x = 1)
#> $x
#> [1] 1

所以R必须处理数据,即使它从未分配任何内容。我知道@ hadleywickham的推文总结的严格的形式:

enter image description here

但我不确定它在这里是如何运作的。只是临时名称没有被保留吗? for循环是否始终使用该名称/对象?评估大量代码而不管它是否被分配仍会耗尽内存? (直到gc()被调用,无论何时都是??)

tl; dr - R&#34;如何存储&#34;未分配的打印值?

1 个答案:

答案 0 :(得分:1)

好的,所以我会尽我所能。

首先,tracemem是一个原语。这意味着它不是像你可以从R代码调用的绝大多数R级函数那样的闭包。更具体地说,它是一个BUILTINSXP原语:

> .Internal(inspect(tracemem))
@62f548 08 BUILTINSXP g0c0 [MARK,NAM(1)] 

这意味着当它被调用时,闭包不会被应用(因为它是一个原语)并且它的参数被评估,因为它是一个BUILTINSXP(见{{3 }})。

Closure应用程序是当R对象传递时,函数调用中的参数被分配给调用帧中的适当变量。因此,tracemem不会发生这种情况。相反,它的参数在C级别被评估为SEXP,它永远不会绑定到任何环境中的任何符号,而是直接传递给C级do_tracemem函数。见this section of the R internals manual

这意味着当一个数字常量传递给tracemem(一个有效的调用,通常没有任何理由可以做)时,你得到常量的实际SEXP,而不是一个代表R值变量的值4,传递给do_tracemem。

据我所知,在任何评估框架内(我可能没有精确地使用这个术语,但是for循环中的调用框架和步骤符合条件,个别顶级表达式也是如此),每个评估,例如, &#34; 4L&#34;获得一个全新的SEXP(INTSXP,具体而言),NAMED立即设置为4.在这些帧之间,看起来它们可以共享,但我非常强烈怀疑它可能是内存重用的工件而不是实际共享的SEXP。 / p>

下面的输出似乎证实了记忆重用理论,但我现在没有周期自由确认它。

> for(i in 1:3) {print(tracemem(4L)); print(tracemem(4L))}
[1] "<0x1c3f3b8>"
[1] "<0x1c3f328>"
[1] "<0x1c3f3b8>"
[1] "<0x1c3f328>"
[1] "<0x1c3f3b8>"
[1] "<0x1c3f328>"

希望有所帮助。