循环使用readRDS读取大量RDS文件时出现内存泄漏问题
R循环读取大量RDS文件内存飙升问题解决
当循环读取大量RDS文件时,内存占用会急剧飙升,即便每次迭代执行gc()也无法有效缓解。以下是复现问题的代码:
library(rnaturalearth) library(purrr) library(glue) world <- ne_countries(scale = "large", returnclass = "sf") # 生成测试用RDS文件 walk(1:1000, function(i) { saveRDS(world, file = glue("cache/{i}.rds")) }, .progress = TRUE) # 读取文件时出现内存飙升 walk(1:1000, function(i) { world <- readRDS(glue("cache/{i}.rds")) }, .progress = TRUE)
通过macOS活动监视器和RStudio内存组件监控,循环前后执行gc()的输出如下:
> gc() used (Mb) gc trigger (Mb) limit (Mb) max used (Mb) Ncells 1075674 57.5 2266002 121.1 NA 1281588 68.5 Vcells 1655548 12.7 8388608 64.0 102400 2572972 19.7 > gc() used (Mb) gc trigger (Mb) limit (Mb) max used (Mb) Ncells 1183433 63.3 17322119 925.2 NA 23685502 1265.0 Vcells 2534823 19.4 791702570 6040.3 102400 1137590813 8679.2
问题原因
核心问题出在purrr::walk的执行环境特性:每次迭代中创建的world对象会被保留在walk的调用环境中,不会被自动回收。即便你在迭代函数内覆盖world变量,旧对象的引用依然存在,导致GC无法释放内存。此外,R的垃圾回收是惰性触发的,即便手动调用gc(),如果对象还有未被移除的引用,也无法回收。
解决办法
1. 改用基础R的for循环
基础for循环的变量作用域更简洁,每次迭代后旧的world对象会失去引用,更容易被GC回收:
# 创建cache目录(如果不存在) if (!dir.exists("cache")) dir.create("cache") for (i in 1:1000) { world <- readRDS(glue("cache/{i}.rds")) # 在这里添加对world的处理逻辑 rm(world) # 主动移除对象引用 gc(verbose = FALSE) # 可选:主动触发垃圾回收 }
2. 在walk迭代内主动清理对象
在walk的迭代函数中,主动删除对象并触发GC,确保旧对象失去所有引用:
walk(1:1000, function(i) { world <- readRDS(glue("cache/{i}.rds")) # 处理world... rm(world) # 删除当前环境中的对象引用 gc(verbose = FALSE) # 触发回收 }, .progress = TRUE)
3. 使用local()隔离局部环境
用local()创建独立的局部环境,迭代结束后环境销毁,里面的对象自动失去引用:
walk(1:1000, function(i) { local({ world <- readRDS(glue("cache/{i}.rds")) # 处理world... }) # 局部环境执行完毕,world被标记为可回收 }, .progress = TRUE)
内容的提问来源于stack exchange,提问作者user3170702
相关产品推荐
相关产品推荐

