我正在编写一些代码来将非常大的平面文本文件解析为持久保存到数据库的对象。这正在处理文件的各个部分(即,如果我'排在前2000行),但是当我尝试处理完整文件时,我遇到了java.lang.OutOfMemoryError: Java heap space
错误。
我正在使用BufferedReader逐行读取文件,我的印象是这否定了将整个文本文件加载到内存中的要求。希望我的代码是相当不言自明的。我已经通过Eclipse Memory Analyzer运行我的代码,它通知我:
线程java.lang.Thread @ 0x27ee0478 main保存局部变量,总大小为69,668,888(98.76%)字节。
内存被累积在由“< system class loader>”加载的“char []”的一个实例中。**
有用的评论非常感谢!
乔纳森
public ArrayList<Statement> parseGMIFile(String filePath)
throws IOException {
ArrayList<Statement> statements = new ArrayList<Statement>();
// Statement Properties
String sAccount = "";
String sOffice = "";
String sFirm = "";
String sDate1 = "";
String sDate2 = "";
Date date = new Date();
StringBuffer sData = new StringBuffer();
BufferedReader in = new BufferedReader(new FileReader(filePath));
String line;
String prevCode = "";
int lineCounter = 1;
int globalLineCounter = 1;
while ((line = in.readLine()) != null) {
// We extract the GMI code from the end of the first line
String newCode = line.substring(GMICODE_START_POS).trim();
// Extract date
if (newCode.equals(prevCode)) {
if (lineCounter == DATE_LINE) {
sDate1 = line.substring(DATE_START_POS, DATE_END_POS).trim();}
if (lineCounter == DATE_LINE2) {
sDate2 = line.substring(DATE_START_POS, DATE_END_POS).trim();}
if (sDate1.equals("")){
sDate1 = sDate2;}
SimpleDateFormat formatter=new SimpleDateFormat("MMM dd, yyyy");
try {
date=formatter.parse(sDate1);
} catch (ParseException e) {
e.printStackTrace();
}
sFirm = line.substring(FIRM_START_POS, FIRM_END_POS);
sOffice = line.substring(OFFICE_START_POS, OFFICE_END_POS);
sAccount = line.substring(ACCOUNT_START_POS,
ACCOUNT_END_POS);
lineCounter++;
globalLineCounter++;
sData.append(line.substring(0, END_OF_DATA)).append("\n");
} else {
// Instantiate New Statement Object
Statement stmt = new Statement(sAccount, sOffice, sFirm,
date, sData.toString());
// Add to collection
statements.add(stmt);
// log.info("-----------NEW STATEMENT--------------");
sData.setLength(0);
lineCounter = 1;
}
prevCode = newCode;
}
return statements;
}
STACKTRACE: Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dbPopulator' defined in class path resource [app-context.xml]: Invocation of init method failed; nested exception is java.lang.OutOfMemoryError: Java heap space at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1401) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:512) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:287) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:557) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:93) at Main.main(Main.java:11) Caused by: java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2882) at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390) at java.lang.StringBuffer.append(StringBuffer.java:224) at services.GMILogParser.parseGMIFile(GMILogParser.java:133) at services.DBPopulator.init(DBPopulator.java:27) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1529) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1468) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1398) ... 12 more
答案 0 :(得分:5)
在启动参数中添加更多内存是恕我直言,这是一个错误。这些参数适用于各种应用。并且可以通过增加gc
次来惩罚。此外,您可能事先不知道尺寸。
您使用MemoryMappedFiles
并查看java.nio.*来执行此操作。这样做可以在读取时加载,并且内存不会放在普通的内存空间中。
通过低水平读取,你可以用可变长度的块来做。速度很重要。如果您的文件很大,可能需要花费太多时间才能阅读。您在Objects
中存储的JVM
数量会使GC
有效并且应用程序会变慢。
来自java参考:
可以将byte buffer
分配为直接缓冲区,在这种情况下,Java虚拟机将尽最大努力直接执行native I/O operations
。
可以通过将文件区域直接映射到内存中来创建byte buffer
,在这种情况下,可以使用MappedByteBuffer类中定义的一些其他与文件相关的操作。
A byte buffer
提供对其内容的访问,可以是任何非布尔基本类型的异构或同构二进制数据序列,可以是big-endian或little-endian字节顺序。
答案 1 :(得分:2)
也许是语句对象变得太大了?如果是这样,也许你应该分批将它持久保存到数据库中而不是一次性完成?
答案 2 :(得分:2)
您的应用程序似乎正在使用VM分配的默认内存(如果我没记错的话,大约64 MB)。由于您的应用程序是特殊用途的,我建议增加应用程序的可用内存(例如,使用java -Xmx256m
运行应用程序将允许它使用最多256 MB的RAM)。您也可以尝试使用服务器VM(java -server yourapp
)运行它,它会尝试稍微优化一下。
答案 3 :(得分:2)
此处可能发生的另一件事: 如果您的文件大于堆的一半并且不包含任何换行符,则.readLine()将尝试读取整个文件并在这种情况下失败。
答案 4 :(得分:2)
-Xmx1024M -XX:MaxPermSize=256M
已解决了我的java.lang.OutOfMemoryError: Java heap space error
。
希望这会奏效。
答案 5 :(得分:0)
代码似乎对我而言。也许我应该用StringBuffer代替String。
字符串在java中非常讨厌,对于它们执行的每个修改,都会创建一个新对象,并且refs可以保留在代码中的任何位置。
通常我使用本地变量读取私有方法中的文件行,只是为了确保没有遗留任何参考字符串。
您要获取的列表是具有String属性的bean列表?如果是这样,请将'em'更改为StringBuffer并重新运行分析。
如果这有助于你,请告诉我。
问候,
微米。
答案 6 :(得分:0)
似乎sData导致溢出。文本中应该有几个(百万?)语句具有相同的GMI代码。
char []的累积意味着String或StringBuilder。因为它在调整StringBuilder大小时失败了,所以应该是原因。
尝试将sData输出到stdout进行调试,看看会发生什么。
答案 7 :(得分:0)
为什么不尝试替换该行(如果使用JDK 6,则在JDK 7中解决了子串内存问题)
String newCode = line.substring(GMICODE_START_POS).trim();
替换行:
String newCode = new String(line.substring(GMICODE_START_POS));
答案 8 :(得分:0)
几个月前我遇到了同样的问题
我使用了Scanner
类:
Scanner scanner = new Scanner(file);
而不是:
BufferedReader in = new BufferedReader(new FileReader(filePath));