Xử lý java.lang.OutOfMemoryError khi ghi vào Excel từ R


82

Các xlsxgói có thể được sử dụng để đọc và viết bảng tính Excel từ R. Thật không may, ngay cả đối với các bảng tính khá lớn, java.lang.OutOfMemoryErrorcó thể xảy ra. Đặc biệt,

Lỗi trong .jcall ("RJavaTools", "Ljava / lang / Object;", "invokeMethod", cl,:
java.lang.OutOfMemoryError: Java heap space

Lỗi trong .jcall ("RJavaTools", "Ljava / lang / Object;", "newInstance", .jfindClass (class),:
java.lang.OutOfMemoryError: GC overheadhead limit

(Các trường hợp ngoại lệ liên quan khác cũng có thể xảy ra nhưng hiếm hơn.)

Một câu hỏi tương tự đã được hỏi về lỗi này khi đọc bảng tính.

Nhập tệp xlsx lớn vào R?

Ưu điểm chính của việc sử dụng bảng tính Excel làm phương tiện lưu trữ dữ liệu so với CSV là bạn có thể lưu trữ nhiều trang tính trong cùng một tệp, vì vậy ở đây chúng tôi xem xét danh sách các khung dữ liệu được ghi một khung dữ liệu trên mỗi trang tính. Tập dữ liệu mẫu này chứa 40 khung dữ liệu, mỗi khung có hai cột với tối đa 200 nghìn hàng. Nó được thiết kế đủ lớn để có vấn đề, nhưng bạn có thể thay đổi kích thước bằng cách thay đổi 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))

Phương pháp tự nhiên để ghi điều này vào tệp là tạo một sổ làm việc bằng cách sử dụng createWorkbook, sau đó lặp qua mỗi khung dữ liệu gọi createSheetaddDataFrame. Cuối cùng sổ làm việc có thể được ghi vào tệp bằng cách sử dụng saveWorkbook. Tôi đã thêm tin nhắn vào vòng lặp để giúp bạn dễ dàng nhìn thấy nơi nó rơi xuống.

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

Chạy tính năng này ở chế độ 64-bit trên máy có RAM 8GB, nó sẽ GC overhead limit exceededxảy ra lỗi khi chạy addDataFramelần đầu tiên.

Làm cách nào để ghi các tập dữ liệu lớn vào bảng tính Excel xlsx?

Câu trả lời:


78

Đây là sự cố đã biết: http://code.google.com/p/rexcel/issues/detail?id=33

Trong khi chưa được giải quyết, trang vấn đề liên kết đến giải pháp của Gabor Grothendieck đề xuất rằng kích thước heap nên được tăng lên bằng cách đặt java.parameterstùy chọn trước khi rJavagói được tải. ( rJavalà một phụ thuộc của xlsx.)

options(java.parameters = "-Xmx1000m")

Giá trị 1000là số megabyte RAM để cho phép sử dụng Java heap; nó có thể được thay thế bằng bất kỳ giá trị nào bạn thích. Các thử nghiệm của tôi với điều này cho thấy rằng các giá trị lớn hơn sẽ tốt hơn và bạn có thể vui vẻ sử dụng toàn quyền RAM của mình. Ví dụ: tôi nhận được kết quả tốt nhất bằng cách sử dụng:

options(java.parameters = "-Xmx8000m")

trên máy có RAM 8GB.

Cải tiến hơn nữa có thể đạt được bằng cách yêu cầu thu gom rác trong mỗi lần lặp lại của vòng lặp. Như đã lưu ý bởi @gjabel, việc thu gom rác R có thể được thực hiện bằng cách sử dụng gc(). Chúng ta có thể định nghĩa một hàm thu gom rác Java gọi System.gc()phương thức Java :

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

Sau đó, vòng lặp có thể được cập nhật thành:

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

Với cả hai bản sửa lỗi mã này, mã đã chạy xa như i = 29trước khi gặp lỗi.

Một kỹ thuật mà tôi đã thử không thành công là sử dụng write.xlsx2để ghi nội dung vào tệp ở mỗi lần lặp. Đoạn mã này chậm hơn đoạn mã khác và nó rơi vào lần lặp thứ 10 (nhưng ít nhất một phần nội dung đã được ghi vào tệp).

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

38
Toàn bộ vấn đề này bây giờ có thể được giải quyết bằng cách hoán đổi xlsxgói cho openxlsxgói, gói phụ thuộc vào Rcppchứ không phải Java.
Richie Cotton

4
readxllà một sự thay thế C / C ++ mới khác có vẻ đầy hứa hẹn.
Richie Cotton

1
tiếc là tôi đã tìm thấy cả hai cách đó khá rác để phát hiện và đọc ngày - cả hai kết thúc trong mớ hỗn độn không thể sửa đó là định dạng ngày tháng Excel: \
MichaelChirico

2
@RichieCotton, thay thế hay. Tuy nhiên, openxlsx không thể đọc các tệp .xls hoặc .xlm! (Định dạng file excel 2007).
Espanta

gọi options(java.parameters = "-Xmx8000m")trước khi tải rJava, xlsxjars, xlsxgiải quyết Error in .jcall("RJavaTools", "Ljava/lang/Object;", "invokeMethod", cl, : org.apache.poi.POIXMLException: java.lang.reflect.InvocationTargetException Calls: getNetwork ... <Anonymous> -> .jrcall -> .jcall -> .jcheck -> .Call Execution haltedtrong RHEL 6.3 x86_64, java 1.7.0_79 (Oracle), rJava_0.9-7, xlsxjars_0.6.0, xlsx_0.5.7
Nick Đồng

7

Dựa trên câu trả lời @ richie-cotton, tôi thấy rằng việc thêm gc()vào jgcchức năng đã giữ mức sử dụng CPU ở mức thấp.

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

forVòng lặp trước của tôi vẫn gặp khó khăn với jgcchức năng ban đầu , nhưng với lệnh bổ sung, tôi không còn GC overhead limit exceededgặp thông báo lỗi nữa.


-1

Bạn cũng có thể sử dụng gc () bên trong vòng lặp nếu bạn đang viết từng hàng một. gc () là viết tắt của từ gom rác. gc () có thể được sử dụng trong mọi trường hợp có vấn đề về bộ nhớ.


-1

Giải pháp cho lỗi trên: Vui lòng sử dụng mã r - được đề cập bên dưới:

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

Và, hãy thử nhập lại tệp và bạn sẽ không gặp bất kỳ lỗi nào vì nó hoạt động với tôi.


Hai nhận xét: xlConnect có cùng một vấn đề. Và quan trọng hơn, việc bảo ai đó sử dụng một thư viện khác không phải là giải pháp cho vấn đề với thư viện đang được tham chiếu. Mục tiêu ở đây là ở trong gói xlsx. Có các chủ đề khác dành cho XLConnect.
Michael Tuchman

-1

Tôi đang gặp sự cố với write.xlsx () thay vì đọc .... nhưng sau đó nhận ra rằng tôi đã vô tình chạy 32bit R. Việc hoán đổi nó sang 64bit đã khắc phục được sự cố.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.