减少 java 中的内存使用
Reduce memory usage in java
我有一个 Web 服务 Java 程序,它从数据库中读取 13,000,000 个日期,例如 '08-23-2016 12:54:44' 作为字符串。我的开发环境是Java8,MySQL5.7,tomcat8,声明了一个字符串数组String[]数据来存放。我使用 Guice 将数据数组的初始值注入为空。但是,内存占用仍然很大。这是我的代码:
String[] data;//size is 1,000,000
void generateDataWrapper(String params) {
//read over 13000000 dates string
ResultSet rs = mySQLCon.readData(params);
clearData(data);//set to empty string
int index = 0;
while(rs.next()) {
data[index++] = rs.getString("date");
if (index == (size - 1)) {//calculate every 1,000,000 total 13 times
//calculate statistics
...
//reset all to empty string
clearData(data);
index = 0;
}
}
}
//mySQLCon. readData function
ResultSet readData(String params) {
try {
String query = generateQuery(params);
Statement postStmt = connection.createStatement();
ResultSet rs = postStmt.executeQuery(query);
return rs;
} catch (Exception e) {
}
return null;
}
如果我调用这个函数一次,内存达到12G,如果我再次调用,内存达到20G,第三次内存达到25G并抛出'java.lang.OutOfMemoryError: GC overhead limit exceeded'错误com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2174)
这是错误消息的一部分:
java.lang.OutOfMemoryError: GC overhead limit exceeded
com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2174)
com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1964)
com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3316)
com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:463)
com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3040)
com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2288)
com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2681)
com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2547)
com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2505)
com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1370)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
我已将垃圾收集算法更改为:
-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode
但这没有帮助。
我试过把数据改成静态变量,还是会出现这个问题。
目前 JVM 堆是 8g,tomcat 内存是 24g,但是,我认为增加内存不会解决问题。
我不明白为什么每次调用这个函数我的内存还在增加,谁能给我一些建议吗?
必须关闭像 ResultSet
这样的已用资源以释放底层系统资源。这可以像 try (ResultSet resultSet =...)
.
一样自动完成 declaring the resources in a try-block
当 ResultSet
请求时,您可以尝试 fetch only a limited number of rows from database 而不是立即全部请求。
当对象不再被引用时,它们就有资格进行垃圾回收。因此,只要您的数组对象被引用,它就会以其整个大小保存在内存中。如果不再引用它并且 VM 运行 内存不足,它将能够处理数组对象,可能避免 OutOfMemoryError。
异常高的内存占用可以通过creating a heap dump and exploring it in the tool jvisualvm of the JDK来分析。
此外,您可以将字符串数组更改为长数组,因为字符串会占用大量内存。在您的情况下,日期字符串的大小为 38 字节( 19 char * 2 字节),而 long 仅占用 8 字节的内存。
long[] data;//size is 1,000,000
void generateDataWrapper(String params) {
//read over 13000000 dates string
ResultSet rs = mySQLCon.readData(params);
clearData(data);//set to empty string
int index = 0;
SimpleDateFormat formater = new SimpleDateFormat("MM-dd-YYYY HH:mm:ss");
while(rs.next()) {
try{
Date date = formater.parse(rs.getString("date"));
data[index++] = date.getTime();
}catch(ParseException pe) {
pe.printStackTrace();
}
if (index == (size - 1)) {//calculate every 1,000,000 total 13 times
//calculate statistics
...
//reset all to empty string
clearData(data);
index = 0;
}
}
}
无论你在哪里需要你的字符串,你都可以用下面的方法解析它
SimpleDateFormat formater = new SimpleDateFormat("MM-dd-YYYY HH:mm:ss");
Date date = new Date(data[i]);
String dateString = formater.format(date);
首先,感谢您的所有建议。我已经通过阅读 mm759 弄清楚了这一点,并意识到我在完成阅读后忘记关闭 ResultSet。我加上rs.close()之后,每次都用同样的时间完成,虽然内存会达到我设置的最大内存。
我有一个 Web 服务 Java 程序,它从数据库中读取 13,000,000 个日期,例如 '08-23-2016 12:54:44' 作为字符串。我的开发环境是Java8,MySQL5.7,tomcat8,声明了一个字符串数组String[]数据来存放。我使用 Guice 将数据数组的初始值注入为空。但是,内存占用仍然很大。这是我的代码:
String[] data;//size is 1,000,000
void generateDataWrapper(String params) {
//read over 13000000 dates string
ResultSet rs = mySQLCon.readData(params);
clearData(data);//set to empty string
int index = 0;
while(rs.next()) {
data[index++] = rs.getString("date");
if (index == (size - 1)) {//calculate every 1,000,000 total 13 times
//calculate statistics
...
//reset all to empty string
clearData(data);
index = 0;
}
}
}
//mySQLCon. readData function
ResultSet readData(String params) {
try {
String query = generateQuery(params);
Statement postStmt = connection.createStatement();
ResultSet rs = postStmt.executeQuery(query);
return rs;
} catch (Exception e) {
}
return null;
}
如果我调用这个函数一次,内存达到12G,如果我再次调用,内存达到20G,第三次内存达到25G并抛出'java.lang.OutOfMemoryError: GC overhead limit exceeded'错误com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2174)
这是错误消息的一部分:
java.lang.OutOfMemoryError: GC overhead limit exceeded
com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2174)
com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1964)
com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3316)
com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:463)
com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3040)
com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2288)
com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2681)
com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2547)
com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2505)
com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1370)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
我已将垃圾收集算法更改为: -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode 但这没有帮助。 我试过把数据改成静态变量,还是会出现这个问题。 目前 JVM 堆是 8g,tomcat 内存是 24g,但是,我认为增加内存不会解决问题。
我不明白为什么每次调用这个函数我的内存还在增加,谁能给我一些建议吗?
必须关闭像
ResultSet
这样的已用资源以释放底层系统资源。这可以像try (ResultSet resultSet =...)
. 一样自动完成 declaring the resources in a try-block
当
ResultSet
请求时,您可以尝试 fetch only a limited number of rows from database 而不是立即全部请求。当对象不再被引用时,它们就有资格进行垃圾回收。因此,只要您的数组对象被引用,它就会以其整个大小保存在内存中。如果不再引用它并且 VM 运行 内存不足,它将能够处理数组对象,可能避免 OutOfMemoryError。
异常高的内存占用可以通过creating a heap dump and exploring it in the tool jvisualvm of the JDK来分析。
此外,您可以将字符串数组更改为长数组,因为字符串会占用大量内存。在您的情况下,日期字符串的大小为 38 字节( 19 char * 2 字节),而 long 仅占用 8 字节的内存。
long[] data;//size is 1,000,000
void generateDataWrapper(String params) {
//read over 13000000 dates string
ResultSet rs = mySQLCon.readData(params);
clearData(data);//set to empty string
int index = 0;
SimpleDateFormat formater = new SimpleDateFormat("MM-dd-YYYY HH:mm:ss");
while(rs.next()) {
try{
Date date = formater.parse(rs.getString("date"));
data[index++] = date.getTime();
}catch(ParseException pe) {
pe.printStackTrace();
}
if (index == (size - 1)) {//calculate every 1,000,000 total 13 times
//calculate statistics
...
//reset all to empty string
clearData(data);
index = 0;
}
}
}
无论你在哪里需要你的字符串,你都可以用下面的方法解析它
SimpleDateFormat formater = new SimpleDateFormat("MM-dd-YYYY HH:mm:ss");
Date date = new Date(data[i]);
String dateString = formater.format(date);
首先,感谢您的所有建议。我已经通过阅读 mm759 弄清楚了这一点,并意识到我在完成阅读后忘记关闭 ResultSet。我加上rs.close()之后,每次都用同样的时间完成,虽然内存会达到我设置的最大内存。