Sic*_*abí 1 memory documentation r cons
根据 R 4.1.0 文档的 Memory{base} 帮助页面,R 为“固定”和“可变”大小的对象保留两个单独的内存区域。据我了解,可变大小的对象是用户可以在工作环境中创建的对象:向量、列表、数据框等。但是,当引用固定大小的对象时,文档相当晦涩:
[固定大小的对象]被分配为 cons 单元数组(Lisp 程序员会知道它们是什么,其他人可能会认为它们是语言本身的构建块、解析树等)[.]
有人可以提供一个存储在 cons 单元中的固定大小对象的示例吗?为了进一步参考,我知道该函数memory.profile()给出了 cons 单元的使用情况。例如,在我的会话中,这看起来像:
> memory.profile()
NULL symbol pairlist closure environment promise language
1 23363 623630 9875 2619 13410 200666
special builtin char logical integer double complex
47 696 96915 16105 107138 10930 22
character ... any list expression bytecode externalptr
130101 2 0 50180 1 42219 3661
weakref raw S4
1131 1148 1132
Run Code Online (Sandbox Code Playgroud)
这些计数在数字上和概念上代表什么?例如,是否logical: 16105引用存储在 R 源代码/二进制文件中的 16,105 个逻辑对象(字节?、单元格?)?
我的目的是更好地了解 R 如何在给定会话中管理内存。最后,我想我确实理解Lisp 和 R 中的cons 单元是什么,但如果这个问题的答案需要首先解决这个概念,我认为从那里开始也许不会有什么坏处。
在 C 级别,R 对象只是指向称为“节点”的内存块的指针。每个节点都是一个C struct,可以是 aSEXPREC或 a VECTOR_SEXPREC。VECTOR_SEXPREC适用于类似向量的对象,包括字符串、原子向量、表达式向量和列表。SEXPREC适用于所有其他类型的对象。
该SEXPREC结构体具有三个连续的段:
该VECTOR_SEXPREC结构体具有上面的段 (1) 和 (2),后跟:
该VECTOR_SEXPREC结构后面是一个至少跨越8+n*sizeof(<type>)字节的内存块,其中n是相应向量的长度。该块由 8 字节前导缓冲区、向量“数据”(即向量的元素)以及有时的尾随缓冲区组成。
总之,非向量存储为跨越 32 或 56 字节的节点,而向量存储为跨越 28 或 36 字节的节点,后跟大小与元素数量大致成比例的数据块。因此,节点的大小大致固定,而矢量数据需要可变的内存量。
R 在称为 Ncell(或 cons cells)的块中为节点分配内存,在称为 Vcell 的块中为向量数据分配内存。根据?Memory,每个Ncell在32位系统上为28字节,在64位系统上为56字节,每个Vcell为8字节。因此,这一行?Memory:
R 为固定和可变大小的对象维护单独的区域。
实际上指的是节点和向量数据,而不是 R 对象本身。
memory.profile给出内存中所有 R 对象使用的 cons 单元数,按对象类型分层. 因此sum(memory.profile())将大致等于gc(FALSE)[1L, "used"],它给出了垃圾收集后正在使用的 cons 单元的总数。
gc(FALSE)
## used (Mb) gc trigger (Mb) limit (Mb) max used (Mb)
## Ncells 273996 14.7 667017 35.7 NA 414424 22.2
## Vcells 549777 4.2 8388608 64.0 16384 1824002 14.0
sum(memory.profile())
## [1] 273934
Run Code Online (Sandbox Code Playgroud)
当您分配新的 R 对象时,所报告的正在使用的 Ncell 和 Vcell 数量gc将会增加。例如:
gc(FALSE)[, "used"]
## Ncells Vcells
## 273933 549662
x <- Reduce(function(x, y) call("+", x, y), lapply(letters, as.name))
x
## a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p +
## q + r + s + t + u + v + w + x + y + z
gc(FALSE)[, "used"]
## Ncells Vcells
## 330337 676631
Run Code Online (Sandbox Code Playgroud)
您可能想知道为什么使用的 Vcell 数量增加,因为这x是一个语言对象,而不是向量。原因是节点是递归的:它们包含指向其他节点的指针,这些节点很可能是向量节点。此处,分配 Vcell 的部分原因是 in 中的每个符号都x指向一个字符串(+to "+"、ato"a"等),而每个字符串都是字符向量。(也就是说,令人惊讶的是,在这种情况下需要约 125000 个 Vcell。这可能是Reduce和lapply调用的产物,但我目前不太确定。)
一切都有点分散:
?Memory,,,,, .?`Memory-limits`?gc?memory.profile?object.size