我试图一次插入大约190万行,作为mysql的批量插入。代码的工作方式就像一个咒语,行数不超过170万行,但我得到了gc开销和/或有时超过180万行的java堆空间错误。
代码:
try {
// triples is of type ArrayList<String>
String[] tripleToInsert= null;
for (int i = 0; i < triples.size(); i++) {
count++;
tripleToInsert= triples.get(i).split("\\s+");
/**Insert <s,p,o> into Triples table**/
preparedStmt.setString(1, tripleToInsert[0].trim());
preparedStmt.setString(2, tripleToInsert[1].trim());
preparedStmt.setString(3, tripleToInsert[2].trim());
preparedStmt.addBatch();
preparedStmt.clearParameters();
tripleToInsert=null;
}
}
catch(OutOfMemoryError e)
{
System.out.println("OOM error: IN LOADING TO DB function on loop count: " + count);
e.printStackTrace();
}
String preEndTime= new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime());
System.out.println("Preprocessing Ended:" + preEndTime);
//Insert start time
String startTime= new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime());
System.out.println("Insert Started:" + startTime);
// execute the prepared statement as a batch
long[] results = preparedStmt.executeLargeBatch();
System.out.println("Update Count size: "+ results.length);
//Insert end time
String endTime = new SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().getTime());
System.out.println("Insert Completed:" + endTime);
遇到错误:
OOM error: IN LOADING TO DB function on loop count: 1888076
java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.mysql.jdbc.SingleByteCharsetConverter.toBytesWrapped(SingleByteCharsetConverter.java:230)
at com.mysql.jdbc.StringUtils.getBytesWrapped(StringUtils.java:652)
at com.mysql.jdbc.PreparedStatement.setString(PreparedStatement.java:4005)
at LoadNTriplesByScript.loadTriplesByBatches(LoadNTriplesByScript.java:282)
at LoadNTriplesByScript.insertNTriplesToDB(LoadNTriplesByScript.java:137)
at LoadNTriplesByScript.main(LoadNTriplesByScript.java:77)
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Object.clone(Native Method)
at java.util.TimeZone.clone(TimeZone.java:738)
at sun.util.calendar.ZoneInfo.clone(ZoneInfo.java:647)
at java.util.TimeZone.getDefault(TimeZone.java:625)
at java.text.SimpleDateFormat.initializeCalendar(SimpleDateFormat.java:657)
at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:601)
at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:580)
at LoadNTriplesByScript.loadTriplesByBatches(LoadNTriplesByScript.java:330)
at LoadNTriplesByScript.insertNTriplesToDB(LoadNTriplesByScript.java:137)
at LoadNTriplesByScript.main(LoadNTriplesByScript.java:77)
eclipse版本:oxygen.1a发行版(4.7.1a)
java堆大小:-xms2048m-xmx3072m
我不确定这是由字符串操作(split()方法)引起的,还是preparedstatement addbatch()方法对每个批的行数有限制。我设置了autocommit(false),然后在执行preparedstatement之后提交()。
注意:我读过很多关于gc开销和java堆错误的文章。任何关于为什么会发生这种情况的建议或建议都是值得赞赏的。
1条答案
按热度按时间g6baxovj1#
很有可能,你的内存用完了。该消息是误导性的,因为高gc开销通常是由于没有要收集的内容(所有内存都在使用中)而导致的。
批处理的原因是避免了每个命令的高开销。关于性能,使用1000行或1000000行的批处理几乎不重要。所以尝试小批量。
如果您的应用程序可以在一段时间内看到不完整的数据,也可以尝试较小的提交。
请注意,您在内存中保留了
triples
把一个分开tripleToInsert
. 对于1.8m行和3GB的总容量,每行使用的字节数可以少于1500字节。假设一个字符在java中是两个字节,假设您要存储它两次,而且总是有一些开销,因此它可能每行的字符数小于300个(只是猜测)。你基本上做对了,只是这么大的一批需要很多内存。如果这只是一个导入,那么您可以通过在使用未拆分的三元组时清除它们来节省一些内存。
有时可以使用db提供的内置csv导入。