【问题标题】:Handling java.lang.OutOfMemoryError when writing to Excel from R从 R 写入 Excel 时处理 java.lang.OutOfMemoryError
【发布时间】:2014-03-23 03:46:12
【问题描述】:

xlsx 包可用于从 R 读取和写入 Excel 电子表格。不幸的是,即使对于中等大小的电子表格,java.lang.OutOfMemoryError 也可能出现。特别是,

.jcall("RJavaTools", "Ljava/lang/Object;", "invokeMethod", cl, 中的错误:
java.lang.OutOfMemoryError: Java 堆空间

.jcall("RJavaTools", "Ljava/lang/Object;", "newInstance", .jfindClass(class), 中的错误:
java.lang.OutOfMemoryError: 超出 GC 开销限制

(其他相关的例外也是可能的,但比较少见。)

在阅读电子表格时,有人就这个错误提出了类似的问题。

Importing a big xlsx file into R?

与 CSV 相比,使用 Excel 电子表格作为数据存储介质的主要优点是您可以在同一个文件中存储多张工作表,因此这里我们考虑将数据框列表写入每个工作表一个数据框。此示例数据集包含 40 个数据帧,每个数据帧有两列,最多 200k 行。它被设计得足够大,不会有问题,但您可以通过更改 n_sheetsn_rows 来更改大小。

library(xlsx)
set.seed(19790801)
n_sheets <- 40
the_data <- replicate(
  n_sheets,
  {
    n_rows <- sample(2e5, 1)
    data.frame(
      x = runif(n_rows),
      y = sample(letters, n_rows, replace = TRUE)
    )
  },
  simplify = FALSE
)
names(the_data) <- paste("Sheet", seq_len(n_sheets))

将其写入文件的自然方法是使用createWorkbook 创建一个工作簿,然后循环调用createSheetaddDataFrame 的每个数据帧。最后,可以使用saveWorkbook 将工作簿写入文件。我在循环中添加了消息,以便更容易看到它在哪里跌落。

wb <- createWorkbook()  
for(i in seq_along(the_data))
{
  message("Creating sheet", i)
  sheet <- createSheet(wb, sheetName = names(the_data)[i])
  message("Adding data frame", i)
  addDataFrame(the_data[[i]], sheet)
}
saveWorkbook(wb, "test.xlsx")  

在具有 8GB RAM 的机器上以 64 位运行它,它在第一次运行 addDataFrame 时抛出 GC overhead limit exceeded 错误。

如何使用xlsx 将大型数据集写入 Excel 电子表格?

【问题讨论】:

    标签: r xlsx


    【解决方案1】:

    这是一个已知问题: http://code.google.com/p/rexcel/issues/detail?id=33

    虽然未解决,但 Gabor Grothendieck 的问题页面 links to a solution 建议应通过在加载 rJava 包之前设置 java.parameters 选项来增加堆大小。 (rJavaxlsx 的依赖项。)

    options(java.parameters = "-Xmx1000m")
    

    1000 是允许Java 堆使用的RAM 的兆字节数;它可以替换为您喜欢的任何值。我对此的实验表明,值越大越好,您可以愉快地使用您的全部 RAM 权利。例如,我使用以下方法获得了最佳结果:

    options(java.parameters = "-Xmx8000m")
    

    在具有 8GB RAM 的机器上。

    可以通过在循环的每次迭代中请求垃圾收集来获得进一步的改进。正如@gjabel 所指出的,可以使用gc() 执行R 垃圾回收。我们可以定义一个调用JavaSystem.gc()方法的Java垃圾回收函数:

    jgc <- function()
    {
      .jcall("java/lang/System", method = "gc")
    }    
    

    然后循环可以更新为:

    for(i in seq_along(the_data))
    {
      gc()
      jgc()
      message("Creating sheet", i)
      sheet <- createSheet(wb, sheetName = names(the_data)[i])
      message("Adding data frame", i)
      addDataFrame(the_data[[i]], sheet)
    }
    

    通过这两个代码修复,代码运行到i = 29,然后引发错误。

    我尝试过的一种技术是使用write.xlsx2 在每次迭代时将内容写入文件,但未成功。这比其他代码慢,并且在第 10 次迭代时失败(但至少部分内容已写入文件)。

    for(i in seq_along(the_data))
    {
      message("Writing sheet", i)
      write.xlsx2(
        the_data[[i]], 
        "test.xlsx", 
        sheetName = names(the_data)[i], 
        append    = i > 1
      )
    }
    

    【讨论】:

    • 现在可以通过将xlsx 包替换为openxlsx 包来回避整个问题,该包依赖于Rcpp 而不是Java。
    • readxl 是另一种看起来很有前途的新 C/C++ 替代方案。
    • 不幸的是,我发现这两种方法在检测和读取日期方面都非常垃圾——最终都陷入了不可救药的混乱,即 Excel 日期格式:\
    • @RichieCotton,不错的选择。但是,openxlsx 无法读取 .xls 或 .xlm 文件! (2007 excel文件格式)。
    • 在加载 rJavaxlsxjarsxlsx 之前调用 options(java.parameters = "-Xmx8000m") 在 RHEL 6.3 x86_64、java 1.7.0_79 (Oracle)、rJava_0.9-7、xlsxjars_0 中解决了 Error in .jcall("RJavaTools", "Ljava/lang/Object;", "invokeMethod", cl, : org.apache.poi.POIXMLException: java.lang.reflect.InvocationTargetException Calls: getNetwork ... &lt;Anonymous&gt; -&gt; .jrcall -&gt; .jcall -&gt; .jcheck -&gt; .Call Execution halted。 6.0, xlsx_0.5.7
    【解决方案2】:

    在@richie-cotton 答案的基础上,我发现将gc() 添加到jgc 函数可以保持较低的CPU 使用率。

    jgc <- function()
    {
      gc()
      .jcall("java/lang/System", method = "gc")
    }    
    

    我之前的for 循环仍然与原来的jgc 函数作斗争,但是通过额外的命令,我不再遇到GC overhead limit exceeded 错误消息。

    【讨论】:

      【解决方案3】:

      如果您逐行编写,您也可以在循环内使用 gc()。 gc() 代表垃圾收集。 gc() 可用于任何内存问题。

      【讨论】:

        【解决方案4】:

        上述错误的解决方法: 请使用下面提到的 r - 代码:

        detach(package:xlsx)
        detach(package:XLConnect)
        library(openxlsx)
        

        并且,尝试再次导入文件,您不会收到任何错误,因为它适用于我。

        【讨论】:

        • 两个 cmets: xlConnect 有同样的问题。更重要的是,告诉某人使用不同的库并不能解决被引用的问题。这里的目标是留在 xlsx 包中。还有其他专门讨论 XLConnect 的线程。
        【解决方案5】:

        我遇到了write.xlsx() 的问题,而不是阅读....但后来意识到我不小心运行了 32 位 R。将它换成 64 位已经解决了这个问题。

        【讨论】:

          【解决方案6】:

          重启 R 并在加载 R 包之前插入:

           options(java.parameters = "-Xmx2048m")  
          

          options(java.parameters = "-Xmx8000m")
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-11-11
            • 2018-02-23
            • 2022-01-14
            • 2013-12-24
            • 1970-01-01
            • 2010-09-10
            相关资源
            最近更新 更多