繁体   English   中英

减少Java中的内存使用量

[英]Reduce memory usage in java

我有一个Web服务Java程序,它从数据库中读取13,000,000个日期,例如'08 -23-2016 12:54:44'作为字符串。 我的开发环境是Java 8,MySQL 5.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开销限制” 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,但是,我不认为增加内存可以解决问题。

我不明白为什么每次调用此函数时我的记忆仍在增加,有人可以给我一些建议吗?

  1. 必须关闭诸如ResultSet类的使用过的资源,才能释放基础的系统资源。 这可以在try块中自动声明资源,例如try (ResultSet resultSet =...)

  2. 当从ResultSet请求它们时,您可以尝试仅从数据库中获取有限数量的行,而不是立即ResultSet所有这些行。

  3. 当不再引用对象时,它们就有资格进行垃圾回收。 因此,只要引用了数组对象,它的整个大小就会保留在内存中。 如果不再引用它,并且VM内存不足,它将能够处置该数组对象,从而避免OutOfMemoryError。

  4. 可以通过创建堆转储在JDK的jvisualvm工具中对其进行分析来分析意外的高内存使用情况。

另外,由于字符串会消耗大量内存,因此您可以将字符串数组更改为长数组。 在您的情况下,日期字符串的大小为38个字节(19个字符* 2个字节),而长字符串仅占用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()之后,尽管内存将达到我设置的最大内存,但每次都需要花费相同的时间才能完成。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM