대용량 Excel 파일 읽기(50w+ 지원) (3)
대용량 Excel 파일 읽기(50w+ 지원)
1 온라인 메모리 넘침 문제 프레젠테이션
환경 준비
-Xmx500m
) 아날로그 OOM-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d://dump.hprof
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>4.1.0version>
dependency>
<dependency>
<groupId>xercesgroupId>
<artifactId>xercesImplartifactId>
<version>2.11.0version>
dependency>
실행 코드
/**
* excel
*
* @author Leon
* @version 2020/5/5 20:07
*/
public class ReadExcelDemo
{
public static void main(String[] args) throws Exception
{
FileInputStream is = new FileInputStream("d:\\test.xlsx");
XSSFWorkbook wb = new XSSFWorkbook(is);
// TODO with wb
System.out.println("ok");
}
}
실행 결과
java.lang.OutOfMemoryError: Java heap space
Dumping heap to d://dump.hprof ...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.io.ByteArrayOutputStream.(ByteArrayOutputStream.java:77)
at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:147)
at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:121)
at org.apache.poi.openxml4j.util.ZipArchiveFakeEntry.(ZipArchiveFakeEntry.java:47)
at org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource.(ZipInputStreamZipEntrySource.java:53)
at org.apache.poi.openxml4j.opc.ZipPackage.(ZipPackage.java:106)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:301)
at org.apache.poi.ooxml.util.PackageHelper.open(PackageHelper.java:37)
at org.apache.poi.xssf.usermodel.XSSFWorkbook.(XSSFWorkbook.java:303)
at com.concurrent.excel.ReadExcelDemo.main(ReadExcelDemo.java:20)
dump.hprof 파일 분석
jdk 자체
jvisualvm
를 사용하여 이 파일을 엽니다.다음 그림:[외부 체인 이미지 저장 실패, 원본 사이트에 도난 방지 체인 메커니즘이 있을 수 있으므로 그림을 저장하여 직접 업로드하는 것을 권장합니다(img-arAxXn4Z-1588769902970)(./asserts/001.png)]
원인 요약: xlsx를 처리할 때 데이터를 메모리에 완전히 읽어서 메모리가 넘칩니다.필자의 경험에 의하면 10m의 excel이 백그라운드에서 완전히 처리되는 상황은 적어도 2GB 메모리가 필요하다. 이 확대의 배수는 매우 무섭다. 어쩐지 메모리가 넘치더라니.
2 작은 메모리로 초대형 excel 데이터 읽기 실현
지난 소절에서 우리가 OOM에 포지셔닝된 이유는 이런 상황에 대해 POI 정부도 해결 방안을 제시했다.이벤트 구동 모드(Event API)입니다.
이 코드는 직접 복사해서 사용하거나 조금만 개조하면 프로젝트에서 사용할 수 있다.
/**
* : excel
*
* @author Leon
* @version 2020/5/5 20:07
*/
public class EventReadExcelDemo
{
public static void main(String[] args) throws Exception
{
new EventReadExcelDemo().processOneSheet("d:\\test.xlsx");
System.out.println("ok");
}
public void processOneSheet(String filename) throws Exception {
OPCPackage pkg = OPCPackage.open(filename);
XSSFReader r = new XSSFReader(pkg);
SharedStringsTable sst = r.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
// sheet
InputStream sheet2 = r.getSheet("rId1");
InputSource sheetSource = new InputSource(sheet2);
parser.parse(sheetSource);
sheet2.close();
}
public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
XMLReader parser =
XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser"
);
ContentHandler handler = new SheetHandler(sst);
parser.setContentHandler(handler);
return parser;
}
private static class SheetHandler extends DefaultHandler {
private SharedStringsTable sst;
private String lastContents;
private boolean nextIsString;
private SheetHandler(SharedStringsTable sst) {
this.sst = sst;
}
// handler
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
// c =>
if(name.equals("c")) {
System.out.print(attributes.getValue("r") + " - ");
//
String cellType = attributes.getValue("t");
if(cellType != null && cellType.equals("s")) {
nextIsString = true;
} else {
nextIsString = false;
}
}
lastContents = "";
}
// handler
public void endElement(String uri, String localName, String name)
throws SAXException {
if(nextIsString) {
int idx = Integer.parseInt(lastContents);
lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
nextIsString = false;
}
// v =>
if(name.equals("v")) {
System.out.println(lastContents);
}
}
// handler
public void characters(char[] ch, int start, int length)
throws SAXException {
lastContents += new String(ch, start, length);
}
}
}
실행 결과
....
E153797 - A153798 - xxxxx
B153798 - 2953
C153798 -
D153798 -
...
우리는 15만 줄까지 운행했고 메모리 넘침도 발생하지 않았다는 것을 발견했다.
3 매듭
같은 방법으로 흐르는 xlsx 파일을 읽을 수 있지만 내부 데이터만 읽을 수 있고 수정 작업을 할 수 없습니다.
나중에 큰 파일을 쓰는 방법을 소개할 거예요.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
FTP의 액티브 모드 및 패시브 모드로그인에 성공한 후list 목록이나 데이터를 읽을 때 클라이언트는 랜덤으로 하나의 포트(1024 이상)를 개방하고 포트를 FTP 서버에 보내서 서버 클라이언트에게 주동 모드를 사용하고 개방하라고 알려준다 FTP 서버...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.