如何降低Shinyapps.io上R项目的内存占用?
Shiny应用内存占用异常排查与优化建议
问题背景
我正在把用R编写的Shiny应用部署到Shinyapps.io。在RStudio中,运行应用返回编辑器后,“已用内存”为4.9 GiB;执行gc()后,该数值变化不超过0.02 GiB。执行sum(sapply(ls(), function(x){object.size(get(x))}))得到约62MB的结果,其中包含一个用magick包存储33张低质量图片的magick-image类列表对象(大小15MB),存储这些图片的Github仓库大小为43MB。部署到Shinyapps.io后,必须将“实例大小”设置为8GB否则应用崩溃,Shinyapps.io报告的内存占用超过5GB。RStudio与Shinyapps.io的内存占用报告相近,但比数据存储所需内存高出近两个数量级。随着数据增长,我担心会触及ShinyApps账户的8GB上限。我使用了包括tidyverse在内的若干包,虽知晓其会占用内存,但不足以解释内存占用的大幅增长。
你的观察方式是否正确?
你的观察存在明显局限性:
object.size()仅统计全局环境中用户创建的对象,但Shiny运行时的函数环境、包的内部对象、缓存数据、Shiny自身的运行时组件都不在这个统计范围内,因此和实际进程内存占用差距大是正常的。- RStudio显示的“已用内存”是整个R进程的总占用,包含所有加载的包、临时数据、缓存等,和
object.size()的统计维度完全不同,两者没有直接可比性。
可排查的内存优化方向
1. 包加载优化
- 避免全局加载冗余包:
tidyverse包含多个子包,只加载实际用到的(比如仅用dplyr就不必加载整个tidyverse);用requireNamespace()替代library(),仅在函数内部需要时加载包,减少启动时的内存开销。 - 清理未使用的包:用
codetools::findGlobals(app_server)(替换app_server为你的服务器函数)分析代码,找出未实际调用的包和函数。
2. 图片资源处理
magick存储的是解码后的位图,比磁盘上的压缩文件占用内存多得多。改为按需加载图片:不在全局预加载所有图片,而是在用户触发查看操作时再读取处理,用完后用rm()删除对象并调用gc()释放内存。- 压缩图片:用
magick::image_resize()缩小分辨率,magick::image_compress("JPEG", quality = 50)降低质量,减少单张图片的内存占用。 - 直接引用文件:将图片存在应用的本地目录,Shiny用
img(src = "path/to/image.jpg")直接引用,无需加载为magick-image对象。
3. Shiny运行时内存管理
- 检查全局变量累积:避免在
observe/reactive中往全局对象里持续添加数据,用户相关数据应存在reactiveValues()中;用bindCache()缓存结果时设置合理的过期时间,防止缓存无限增长。 - 关闭不必要的缓存:对不需要重复使用的reactive计算,设置
reactive(..., cache = FALSE)。 - 内存剖析:用
profvis::profvis({runApp()})启动应用并分析,定位占用内存最多的函数或对象,排查内存泄漏点。
4. 部署环境优化
- 在部署脚本中添加
options(shiny.maxRequestSize = 10*1024^2)(限制上传文件大小为10MB),避免用户上传大文件导致内存过载。 - 延迟初始化:把非必要的数据加载、计算逻辑移到用户触发的事件(如按钮点击)中,不要在应用启动时一次性完成。
内容的提问来源于stack exchange,提问作者Eric Krantz
相关产品推荐
相关产品推荐

