我正在开发一个Java程序,该程序将从Sybase数据库获取数据,并使用UCanAccess将其导入Microsoft Access数据库。但是,我目前遇到了一个问题,收到了错误“java.lang.OutOfMemoryError:超出了GC开销限制”。
为了将这种情况放到上下文中,我正在尝试将大约130万条记录导入Access数据库。在导入了大约800000条记录之后,在运行时大约十分钟,以及从Sybase数据库检索到ResultSet之后很长时间,程序当前遇到错误。
我曾试图修改堆大小,但这会导致程序速度明显减慢。请注意,这是一个根据需要多次运行的特殊程序,因此运行时间应为分钟或小时,而根据我的观察,增加堆大小会将运行时间增加到天的数量级。
作为参考,错误发生在主方法中,在名为getRecords的子例程期间(发生此错误的确切代码行因运行而异)。我已经将代码包含到下面的程序中,对部分代码进行了一些小的更改,例如我正在使用的确切查询以及访问数据库的用户名和密码,以免泄露敏感信息。
在我的程序代码中是否有什么可以更改的地方,以减轻垃圾收集器的负载,而不会将运行时间增加到几个小时以上?
编辑:我似乎误解了Java的默认最大堆大小。当我认为通过将堆大小设置为512m来增加堆大小时,我无意中将堆大小减半。当我将堆大小设置为2048m时,我得到了一个java堆空间错误。如果可能的话,我仍然希望在不修改堆大小的情况下解决这个问题。
编辑2:显然,我被误导了,因为我需要处理一些记录。它的大小是我最初认为的两倍,这表明我需要彻底改变我的方法。我将继续接受一个答案,因为这个答案确实带来了巨大的改进。
getRecords方法:
public static void getRecords(SybaseDatabase sdb, AccessDatabase adb)
{
ArrayList<Record> records = new ArrayList<Record>();
StringBuffer sql = new StringBuffer();
Record currentRecord = null;
try{
Statement sybStat = sdb.connection.createStatement();
PreparedStatement resetADB = adb.connection.prepareStatement("DELETE FROM Table");
PreparedStatement accStat = adb.connection.prepareStatement("INSERT INTO Table (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
sql.append(query);//query is a placeholder, as I cannot give out the actual query to the database. I have confirmed that the query itself gives the ResultSet that I am looking for
ResultSet rs = sybStat.executeQuery(sql.toString());
resetADB.executeUpdate();
boolean nextWatch = true;
Integer i = 1;
Record r = new Record();
while(nextWatch)
{
for (int j = 0; j < 1000 && nextWatch; j++)
{
nextWatch = rs.next();
r.setColumn(i, 0);
r.setColumn(rs.getString("B"), 1);
r.setColumn(rs.getString("C"), 2);
r.setColumn(rs.getString("D"), 3);
r.setColumn(rs.getString("E"), 4);
r.setColumn(rs.getString("F"), 5);
r.setColumn(rs.getString("G"), 6);
r.setColumn(rs.getString("H"), 7);
r.setColumn(rs.getString("I"), 8);
r.setColumn(rs.getString("J"), 9);
r.setColumn(rs.getString("K"), 10);
r.setColumn(rs.getInt("L"), 11);
r.setColumn(rs.getString("M"), 12);
r.setColumn(rs.getString("N"), 13);
r.setColumn(rs.getString("O"), 14);
r.setColumn(rs.getString("P"), 15);
records.add(r);
i++;
}
for(int k = 0; k < records.size(); k++)
{
currentRecord = records.get(k);
for(int m = 0; m < currentRecord.getNumOfColumns(); m++)
{
if (currentRecord.getColumn(m) instanceof String)
{
accStat.setString(m + 1, "\"" + currentRecord.getColumn(m) + "\"");
}
else
{
accStat.setInt(m + 1, Integer.parseInt(currentRecord.getColumn(m).toString()));
}
}
accStat.addBatch();
}
accStat.executeBatch();
accStat.clearBatch();
records.clear();
}
adb.connection.commit();
}
catch(Exception e){
e.printStackTrace();
}
finally{
}
}
}
完整代码:
import java.util.*;
import java.sql.*;
import com.sybase.jdbc2.jdbc.SybDriver;//This is an external file that is used to connect to the Sybase database. I will not include the full code here for the sake of space but will provide it upon request.
public class SybaseToAccess {
public static void main(String[] args){
String accessDBPath = "C:/Users/me/Desktop/Database21.accdb";//This is a placeholder, as I cannot give out the exact file path. However, I have confirmed that it points to the correct file on the system.
String sybaseDBPath = "{sybServerName}:{sybServerPort}/{sybDatabase}";//See above comment
try{
AccessDatabase adb = new AccessDatabase(accessDBPath);
SybaseDatabase sdb = new SybaseDatabase(sybaseDBPath, "user", "password");
getRecords(sdb, adb);
}
catch(Exception e){
e.printStackTrace();
}
finally{
}
}
public static void getRecords(SybaseDatabase sdb, AccessDatabase adb)
{
ArrayList<Record> records = new ArrayList<Record>();
StringBuffer sql = new StringBuffer();
Record currentRecord = null;
try{
Statement sybStat = sdb.connection.createStatement();
PreparedStatement resetADB = adb.connection.prepareStatement("DELETE FROM Table");
PreparedStatement accStat = adb.connection.prepareStatement("INSERT INTO Table (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
sql.append(query);//query is a placeholder, as I cannot give out the actual query to the database. I have confirmed that the query itself gives the ResultSet that I am looking for
ResultSet rs = sybStat.executeQuery(sql.toString());
resetADB.executeUpdate();
boolean nextWatch = true;
Integer i = 1;
Record r = new Record();
while(nextWatch)
{
for (int j = 0; j < 1000 && nextWatch; j++)
{
nextWatch = rs.next();
r.setColumn(i, 0);
r.setColumn(rs.getString("B"), 1);
r.setColumn(rs.getString("C"), 2);
r.setColumn(rs.getString("D"), 3);
r.setColumn(rs.getString("E"), 4);
r.setColumn(rs.getString("F"), 5);
r.setColumn(rs.getString("G"), 6);
r.setColumn(rs.getString("H"), 7);
r.setColumn(rs.getString("I"), 8);
r.setColumn(rs.getString("J"), 9);
r.setColumn(rs.getString("K"), 10);
r.setColumn(rs.getInt("L"), 11);
r.setColumn(rs.getString("M"), 12);
r.setColumn(rs.getString("N"), 13);
r.setColumn(rs.getString("O"), 14);
r.setColumn(rs.getString("P"), 15);
records.add(r);
i++;
}
for(int k = 0; k < records.size(); k++)
{
currentRecord = records.get(k);
for(int m = 0; m < currentRecord.getNumOfColumns(); m++)
{
if (currentRecord.getColumn(m) instanceof String)
{
accStat.setString(m + 1, "\"" + currentRecord.getColumn(m) + "\"");
}
else
{
accStat.setInt(m + 1, Integer.parseInt(currentRecord.getColumn(m).toString()));
}
}
accStat.addBatch();
}
accStat.executeBatch();
accStat.clearBatch();
records.clear();
}
adb.connection.commit();
}
catch(Exception e){
e.printStackTrace();
}
finally{
}
}
}
class AccessDatabase{
public Connection connection = null;
public AccessDatabase(String filePath)
throws Exception
{
String dbString = null;
dbString = "jdbc:ucanaccess://" + filePath;
connection = DriverManager.getConnection(dbString);
connection.setAutoCommit(false);
}
}
class Record{
ArrayList<Object> columns;
public
Record(){
columns = new ArrayList<Object>();
columns.add("Placeholder1");
columns.add("Placeholder2");
columns.add("Placeholder3");
columns.add("Placeholder4");
columns.add("Placeholder5");
columns.add("Placeholder6");
columns.add("Placeholder7");
columns.add("Placeholder8");
columns.add("Placeholder9");
columns.add("Placeholder10");
columns.add("Placeholder11");
columns.add("Placeholder12");
columns.add("Placeholder13");
columns.add("Placeholder14");
columns.add("Placeholder15");
columns.add("Placeholder16");
}
<T> void setColumn(T input, int colNum){
columns.set(colNum, input);
}
Object getColumn(int colNum){
return columns.get(colNum);
}
int getNumOfColumns()
{
return columns.size();
}
}
class SybaseDatabase{
public Connection connection;
@SuppressWarnings("deprecation")
public SybaseDatabase(String filePath, String Username, String Password)
throws Exception
{
SybDriver driver;
try
{
driver = (SybDriver)Class.forName("com.sybase.jdbc2.jdbc.SybDriver").newInstance();
driver.setVersion(SybDriver.VERSION_6);
DriverManager.registerDriver(driver);
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
connection = DriverManager.getConnection("jdbc:sybase:Tds:" + filePath, Username, Password);
}
}
如果您想使用更少的内存,您应该在同一时间内处理更少的行,但重用所有可以重用的对象(如准备语句
)
第一:使用ArrayList
第二:在处理之前不要从数据库中加载所有数据,加载一小部分数据并处理它,然后继续。
您可以通过提取代码中处理某些行的部分,并通过限制返回行数来更改查询来实现这一点。
现在,您加载1000行(从索引0到999),处理并提交它们。然后加载1000行(从索引1000到1999),处理并提交它们。然后你继续。在每个行包之间,不要保留任何对先前数据的引用(比如对记录),以避免它们被保存在内存中(比如在必要时它们会被垃圾收集)。
如果您仍然没有足够的内存,我猜您在一些对象上保留了一个引用,这些对象因此没有被垃圾收集,从而导致了内存泄漏问题:您的程序在处理每个数据时需要越来越多的内存。您可以使用一些工具,如jvisualvm(java中提供)来调查内存的使用情况
错误:超出GC开销限制(最大堆:1024MB) 当我从命令提示符进行gradle部署或其他gradle操作时,经常会出现上述错误。 谁能帮我解决这个问题。 项目:Java 建造:格拉德尔 *这与Gradle和Java有关。不仅仅是JAVA
使用当我尝试构建我的项目时,这种错误越来越多: 错误:任务“:app:CompiledEbugJavaWithJavac”执行失败。OutofMemoryError:超出GC开销限制 有什么办法解决这个问题吗?
我正在尝试预处理一个大的txt文件(10G),并将其存储在二进制文件中以备将来使用。当代码运行时,速度会减慢,并以 异常线程"main"java.lang.OutOfMemoryError: GC开销限制超过 输入文件具有以下结构 这是我正在使用的代码: 基本上,它通过in文件并将数据存储到对象HMbicnt(这是一个哈希映射)。一旦在第二列中遇到新值,它应该将对象写入输出文件,释放内存并继续。
我正在做一个Java项目。我从服务器检索pdf并将其转换为图像以将像素与预期文件进行比较。当我只使用20-30个文件运行该程序时,它很好,但当我尝试使用超过30个文件时,它会抛出OutOfMemoryError。 看起来像pdf- 下面是Java堆空间日志的一部分: 以下是GC开销限制超出的日志部分: 此外,第一个Java堆空间错误发生在id22,第二个发生在id33。我在服务器上使用8GB RA
问题内容: 我正在尝试建立一个包含2台服务器的Gridgain集群。 使用 GridDataLoader 将.csv文件中的数据(100万至5000万个数据)加载到Gridgain 。 从加载的数据中找到最小值,最大值,平均值等, 当在Eclipse中作为独立应用程序运行时,我得到正确的输出。 但是,在建立集群(eclipse环境中的2个服务器中的2个节点+我的Eclipse环境中的1个节点)的同
问题内容: 我在一个程序中创建了这个错误,该程序创建了几个(数十万)HashMap对象,每个对象都有几个(15-20)文本条目。这些字符串必须全部收集(不分解成较小的数量),然后再提交给数据库。 根据Sun的说法,该错误发生“如果在垃圾回收上花费了太多时间:如果在垃圾回收上花费了总时间的98%以上,而回收不到2%的堆,则将引发OutOfMemoryError。 ”。 显然,可以使用命令行将参数传递