为什么某些内存地址报告为常量而其他内存地址发生变化?
Why are some memory addresses reported constant while others change?
我一直在尝试使用 data.table::address
或 .Internal(address())
来跟踪内存中的各种对象,但我注意到有些对象 return 每次都使用相同的地址,而其他对象几乎总是不同的。这是怎么回事?
我注意到像列表(data.tables、data.frames 等)这样的对象的地址保持不变(正如这些函数所报告的那样),而如果我尝试通过 [
到列表中,即 address(lst[1])
我几乎每次都会得到不同的结果。另一方面,lst[[1]]
return 是相同的值,address(pi)
等常量的地址保持不变,而 address(1)
是易变的。为什么会这样?
## Create some data.tables of different sizes and plot the addresses
library(data.table)
par(mfrow = c(2,2))
for (i in 2:5) {
dat <- data.table(a=1:10^i)
## Constants
addr1 <- address(dat)
addr2 <- address(dat[[1]])
addr3 <- address(dat$a) # same as addr2
## Vary
addrs <- replicate(5000, address(dat[1]))
plot(density(as.integer(as.hexmode(addrs))), main=sprintf("N: %g", nrow(dat)))
abline(v=as.integer(as.hexmode(c(addr1, addr2, addr3))), col=1:3, lwd=2, lty=1:3)
legend("topleft", c("dat", "dat[[1]]", "dat$a"), col=1:3, lwd=2, lty=1:3)
}
这里有一些我所说的不同尺寸的例子 data.tables。它们只是 address(dat[1])
结果的密度(转换为整数),并且这些行对应于 data.table.
的常量地址
首先,我可以复制你的结果,所以我做了一些调查并深入研究了一些代码。
当您使用 dat[1]
访问 dat
的第一个成员时,您实际上是在创建一个由 data[[1]]
或 dat$a
中的 list
组成的切片。要获取切片,R 首先复制列表,然后 returns 您想要的切片。
所以 - 基本上 - 你会看到你所看到的,因为 []
语法用于索引 returns 包含 dat
[=42= 的第一个元素的切片] 这是 dat$a
的副本,它将位于任意内存位置。
[[]]
语法 returns 对实际列表的引用,即 data.table
或 data.frame
中的列,因此其地址是不变的(或至少直到您更改该列表的成员。
这可能会造成混淆,因为当然 dat[1] = 6
或类似操作会改变数据结构中列表的值。但是,如果您在进行此类更改之前和之后查看 address(dat[[1]])
,您会注意到实际上引用现在指向不同的列表(副本),例如
> dat <- data.table(a=1:10000)
> dat
a
1: 1
2: 2
3: 3
4: 4
5: 5
---
9996: 9996
9997: 9997
9998: 9998
9999: 9999
10000: 10000
> address(dat[[1]])
[1] "000000000CF389D8"
> address(dat[[1]])
[1] "000000000CF389D8"
> dat[1] = 100
> address(dat[[1]])
[1] "000000000D035B38"
> dat
a
1: 100
2: 2
3: 3
4: 4
5: 5
---
9996: 9996
9997: 9997
9998: 9998
9999: 9999
10000: 10000
>
查看 data.frame
(而不是 data.table
)的源代码,执行切片索引 ([]
) is here, whereas the direct indexing ([[]]
) is here. You can see that the latter is simpler and to cut a long story short, the former returns a copy. If you change a slice directly (e.g. dat[1] = 5
), there is some logic here 的代码处理确保数据框现在引用更新后的副本。
我一直在尝试使用 data.table::address
或 .Internal(address())
来跟踪内存中的各种对象,但我注意到有些对象 return 每次都使用相同的地址,而其他对象几乎总是不同的。这是怎么回事?
我注意到像列表(data.tables、data.frames 等)这样的对象的地址保持不变(正如这些函数所报告的那样),而如果我尝试通过 [
到列表中,即 address(lst[1])
我几乎每次都会得到不同的结果。另一方面,lst[[1]]
return 是相同的值,address(pi)
等常量的地址保持不变,而 address(1)
是易变的。为什么会这样?
## Create some data.tables of different sizes and plot the addresses
library(data.table)
par(mfrow = c(2,2))
for (i in 2:5) {
dat <- data.table(a=1:10^i)
## Constants
addr1 <- address(dat)
addr2 <- address(dat[[1]])
addr3 <- address(dat$a) # same as addr2
## Vary
addrs <- replicate(5000, address(dat[1]))
plot(density(as.integer(as.hexmode(addrs))), main=sprintf("N: %g", nrow(dat)))
abline(v=as.integer(as.hexmode(c(addr1, addr2, addr3))), col=1:3, lwd=2, lty=1:3)
legend("topleft", c("dat", "dat[[1]]", "dat$a"), col=1:3, lwd=2, lty=1:3)
}
这里有一些我所说的不同尺寸的例子 data.tables。它们只是 address(dat[1])
结果的密度(转换为整数),并且这些行对应于 data.table.
首先,我可以复制你的结果,所以我做了一些调查并深入研究了一些代码。
当您使用 dat[1]
访问 dat
的第一个成员时,您实际上是在创建一个由 data[[1]]
或 dat$a
中的 list
组成的切片。要获取切片,R 首先复制列表,然后 returns 您想要的切片。
所以 - 基本上 - 你会看到你所看到的,因为 []
语法用于索引 returns 包含 dat
[=42= 的第一个元素的切片] 这是 dat$a
的副本,它将位于任意内存位置。
[[]]
语法 returns 对实际列表的引用,即 data.table
或 data.frame
中的列,因此其地址是不变的(或至少直到您更改该列表的成员。
这可能会造成混淆,因为当然 dat[1] = 6
或类似操作会改变数据结构中列表的值。但是,如果您在进行此类更改之前和之后查看 address(dat[[1]])
,您会注意到实际上引用现在指向不同的列表(副本),例如
> dat <- data.table(a=1:10000)
> dat
a
1: 1
2: 2
3: 3
4: 4
5: 5
---
9996: 9996
9997: 9997
9998: 9998
9999: 9999
10000: 10000
> address(dat[[1]])
[1] "000000000CF389D8"
> address(dat[[1]])
[1] "000000000CF389D8"
> dat[1] = 100
> address(dat[[1]])
[1] "000000000D035B38"
> dat
a
1: 100
2: 2
3: 3
4: 4
5: 5
---
9996: 9996
9997: 9997
9998: 9998
9999: 9999
10000: 10000
>
查看 data.frame
(而不是 data.table
)的源代码,执行切片索引 ([]
) is here, whereas the direct indexing ([[]]
) is here. You can see that the latter is simpler and to cut a long story short, the former returns a copy. If you change a slice directly (e.g. dat[1] = 5
), there is some logic here 的代码处理确保数据框现在引用更新后的副本。