如何找到没有存储在 .GlobalEnv 中的重物?

Mat*_*fou 8 memory workspace r environment-variables

我试图找出哪些对象在我的 R 会话中占用了大量内存,但问题是该对象可能是在未知环境中以未知名称无形创建的。

如果对象是存储在.GlobalEnv或已知的环境中,我可以很容易地使用像一个策略ls(enviro)+get()+object.size()(参见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?
Run Code Online (Sandbox Code Playgroud)

reprex 包(v0.3.0)于 2020 年 11 月 3 日创建

use*_*330 5

我认为没有任何现有的方法可以做到这一点。如果您将 @AllanCameron 的答案与我的评论结合起来,您还可以运行计算ls(y)为的环境y

ns <- loadedNamespaces()
for (x in ns) {
   y <- loadNamespace(x)
   # look at the size of everything in y
}
Run Code Online (Sandbox Code Playgroud)

您仍然找不到所有环境。我认为如果您还检查了可能包含对环境的引用的每个对象(例如每个函数、公式、列表和各种外来对象),那么您就可以做到这一点,但是不遗漏某些内容或多次计算某些内容会很棘手。

编辑添加:实际上,pryr::object_size它在报告附加到对象的环境方面非常聪明,因此我们可以通过搜索名称空间来接近。例如,要查找前 20 个对象:

ns <- loadedNamespaces()
for (x in ns) {
   y <- loadNamespace(x)
   # look at the size of everything in y
}
Run Code Online (Sandbox Code Playgroud)

由reprex 包于 2020 年 11 月 3 日创建(v0.3.0)

我不知道为什么这些方法表会变得如此之大(我怀疑这是因为ggplot2向这些表添加了方法,因此它的环境被捕获);但不知怎的,他们找到了你的物体,因为如果我不创建它,它们就不会那么大。

有关该问题的提示位于第 5 个对象中,列为(即命名空间中ggplot2..store 命名的对象)。不会告诉您查看 中的函数的环境,但至少它可以帮助您入门。.storeggplot2.store

第二次编辑:

这里有一些调整,可以使输出更具可读性。

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
Run Code Online (Sandbox Code Playgroud)

通过这些更改,输出sort(sizes[keep], decreasing = TRUE)开始为

# 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(".__", names(sizes), fixed = TRUE)
sizes <- sizes[keep]
Run Code Online (Sandbox Code Playgroud)


All*_*ron 0

如果你这样做

unlist(lapply(search(), function(y) sapply(ls(y), function(x) object.size(get(x)))))
Run Code Online (Sandbox Code Playgroud)

您将获得搜索路径上所有环境中所有对象的完整列表,包括它们的大小。然后您可以对它们进行排序并找到有问题的对象。