问题描述
我试图在我的R会话中查找哪些对象占用大量内存,但是问题是该对象可能是在未知环境中以不知名的名称无形地创建的。
如果对象存储在.GlobalEnv
或已知环境中,我可以轻松地使用诸如ls(enviro)+get()+object.size()
之类的策略(例如,参见this post上的lsos
)来列出所有对象物体及其大小,使我能够识别较重的物体。
但是,有问题的对象可能不会存储在.GlobalEnv
中,而是可能在由外部包隐式创建的某些晦涩的环境中。在那种情况下,如何确定哪个对象正在使用大量RAM?
最好的案例研究是ggplot2
在专用环境中创建.last_plot
。深入了解一下,可以发现它存储在environment(ggplot2:::.store$get)
中,因此可以找到它并最终将其删除。但是,如果我不知道该位置或先验名称,是否有办法发现内存中某处有一个名为.last_plot
的重物?
pryr::mem_used()
#> 34.7 MB
## example: implicit creation of heavy and hidden object by ggplot
path <- tempfile()
if(!file.exists(path)){
saveRDS(as.data.frame(matrix(rep(1,1e07),ncol=5)),path)
}
pryr::mem_used()
#> 34.9 MB
p1 <- ggplot2::ggplot(readr::read_rds(path),ggplot2::aes(V1))
rm(p1)
pryr::mem_used()
#> 127 MB
## Hidden object is not in .GlobalEnv
ls(.GlobalEnv,all.names = TRUE)
#> [1] "path"
## Here I know where to find it: environment(ggplot2:::.store$get)
ls(all.names = TRUE,envir = environment(ggplot2:::.store$get))
#> [1] ".last_plot"
pryr::object_size(get(".last_plot",environment(ggplot2:::.store$get))$data)
#> 80 MB
## But how could I have found this otherwise?
由reprex package(v0.3.0)于2020-11-03创建
解决方法
我认为没有任何现有的方法可以做到这一点。如果您将@AllanCameron的答案与我的评论相结合,那么您还将在ls(y)
环境下运行y
,
ns <- loadedNamespaces()
for (x in ns) {
y <- loadNamespace(x)
# look at the size of everything in y
}
您仍然找不到所有环境。我认为,如果您还检查了每个可能包含对环境的引用的对象(例如,每个函数,公式,列表和各种奇异对象),都可以这样做,但是不要错过某些东西会很棘手或多次计数。
编辑后添加:实际上,pryr::object_size
在报告对象所连接的环境方面非常聪明,因此我们可以通过搜索名称空间来达成目标。例如,要查找前20个对象:
pryr::mem_used()
#> Registered S3 method overwritten by 'pryr':
#> method from
#> print.bytes Rcpp
#> 35 MB
path <- tempfile()
if(!file.exists(path)){
saveRDS(as.data.frame(matrix(rep(1,1e07),ncol=5)),path)
}
pryr::mem_used()
#> 35.2 MB
p1 <- ggplot2::ggplot(readr::read_rds(path),ggplot2::aes(V1))
rm(p1)
pryr::mem_used()
#> 127 MB
envs <- c(globalenv = globalenv(),sapply(loadedNamespaces(),function(ns) loadNamespace(ns)))
sizes <- lapply(envs,function(e) {
objs <- ls(e,all = TRUE)
sapply(objs,function(obj) pryr::object_size(get(obj,envir = e)))
})
head(sort(unlist(sizes),decreasing = TRUE),20)
#> base..__S3MethodsTable__. utils..__S3MethodsTable__.
#> 96216872 83443704
#> grid..__S3MethodsTable__. ggplot2..__S3MethodsTable__.
#> 80945520 80636768
#> ggplot2..store methods..classTable
#> 80418936 10101152
#> graphics..__S3MethodsTable__. tools..check_packages
#> 9325608 5185880
#> compiler.inlineHandlers methods..genericTable
#> 3444600 2808440
#> Rcpp..__T__show:methods colorspace..__T__show:methods
#> 2474672 2447880
#> Rcpp..RcppClass Rcpp..__C__C++OverloadedMethods
#> 2127584 1990504
#> Rcpp..__C__RcppClass Rcpp..__C__C++Field
#> 1982576 1980176
#> Rcpp..__C__C++Constructor Rcpp..__T__$:base
#> 1979992 1939616
#> tools..install_packages Rcpp..__C__Module
#> 1904032 1899872
由reprex package(v0.3.0)于2020-11-03创建
我不知道为什么这些方法表会这么大(我怀疑是因为ggplot2
向这些表添加了方法,所以它的环境被捕获了);但是它们以某种方式找到了您的对象,因为如果我不创建它们,它们就不会很大。
关于此问题的提示在第5个对象中,列为ggplot2..store
(即.store
名称空间中名为ggplot2
的对象)。不会告诉您查看.store
中函数的环境,但是至少可以帮助您入门。
第二次修改:
这里有一些调整,以使输出更具可读性。
# Unlist first,so we can clean up the names
sizes <- unlist(sizes)
# Replace the first dot with :::
names(sizes) <- sub(".",":::",names(sizes),fixed = TRUE)
# Remove internal R objects
keep <- !grepl(".__",fixed = TRUE)
sizes <- sizes[keep]
进行了这些更改后,sort(sizes[keep],decreasing = TRUE)
的输出开始为
ggplot2:::.store
80418936
base:::.userHooksEnv
47855920
base:::.Options
45016888
utils:::Rprof
44958416
,
如果愿意
unlist(lapply(search(),function(y) sapply(ls(y),function(x) object.size(get(x)))))
您将获得搜索路径上所有环境中所有对象的完整列表,包括它们的大小。然后,您可以对它们进行排序并找到有问题的对象。