从R写入Excel时处理java.lang.OutOfMemoryError

xlsx软件包可用于从R读取和写入Excel电子表格。不幸的是,即使是中等大小的电子表格,也可能发生java.lang.OutOfMemoryError 。 尤其是,

 ## Error in .jcall("RJavaTools", "Ljava/lang/Object;", "invokeMethod", cl, : ## java.lang.OutOfMemoryError: Java heap space ## Error in .jcall("RJavaTools", "Ljava/lang/Object;", "newInstance", .jfindClass(class), : ## java.lang.OutOfMemoryError: GC overhead limit exceeded 

(其他相关例外也是可能的,但更less见)

在阅读电子表格时,也遇到过类似的问题。

导入一个大的xlsx文件到R?

使用Excel电子表格作为CSV数据存储介质的主要优点是可以在同一个文件中存储多个工作表,因此在这里我们考虑一个数据框列表,每个工作表要写入一个数据框。 这个示例数据集包含40个dataframe,每个dataframe包含两列高达200k行的数据。 它被devise成足够大以至于有问题,但是可以通过改变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创build工作簿,然后遍历每个调用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位的速度运行此操作时,会在GC overhead limit exceeded运行addDataFrame时引发GC overhead limit exceeded错误。

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

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

虽然没有解决,问题页面链接到一个由Gabor Grothendieck 的解决scheme ,build议在加载rJava包之前,通过设置java.parameters选项来增加堆大小。 ( rJavaxlsx的依赖。)

 options(java.parameters = "-Xmx1000m") 

1000是允许Java堆的RAM的兆字节数; 它可以replace为你喜欢的任何值。 我的这个实验表明,更大的值是更好的,你可以愉快地使用你的全部RAM权利。 例如,我使用以下方法得到了最好的结果:

 options(java.parameters = "-Xmx8000m") 

在8GB RAM的机器上。

通过在循环的每次迭代中请求垃圾回收可以获得进一步的改进。 正如@gjabel所指出的,R垃圾收集可以使用gc()来执行。 我们可以定义一个调用Java System.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次迭代(但至less部分内容被写入文件)下跌。

 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 ) } 

build立在@ richie棉花的答案,我发现joingc() jgcfunction保持CPU使用率低。

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

我以前的for循环仍然与原来的jgc函数挣扎,但有了额外的命令,我不再遇到GC overhead limit exceeded错误消息。

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

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

而且,尝试再次导入文件,你不会得到任何错误,因为它为我工作。

如果您一行一行地写,也可以在循环中使用gc()。 gc()代表垃圾收集。 gc()可以用在任何情况下的内存问题。