传递给另一个线程的大型JDBC ResultSet不释放JVM内存

时间:2017-06-30 16:15:48

标签: java jdbc memory-management

我有一个程序发出一个查询,该查询在30个查询中的每个查询的结果集中返回大约150k数据点。我正在尝试清理处理内存,将JVM推向130MB RAM。有两个线程,一个发出数据库查询并获取结果,另一个执行结果处理。

第一个线程获取结果集并将其深层复制到List>中,该列表放在线程之间的共享队列中。第二个线程取消了它,然后将其设置为null。 (目前用于内存使用测试)。第一个线程围绕DB调用,最后将语句和结果集设置为null。注释掉队列和第二个线程,内存使用量为70MB。但是当我把它重新放入时,内存保持在130MB的使用率。任何想法为什么它不会回到70?

 Statement stmt = null;
        ResultSet rs = null;
        try
        {
            if (conn.isValid(DB_TIMEOUT))
            {

                String query = msg.getQuery();
                Object request = msg.getRequest();

                // errorLog.debug("Query Issued to Trend Database: " + query);
                stmt = conn.createStatement();
                if (!query.isEmpty())
                {
                    stmt.execute(query);
                    rs = stmt.getResultSet();
                }
                TrendApp.resultSetProcQueue.put(new ResultData((jTrendDataRequest) request, resultSetToArrayList(rs)));
} catch (SQLException e)
        {
            errorLog.error("Failed to issue database command: " + e);
        } finally
        {
            if (stmt != null)
            {
                try
                {
                    stmt.close();
                } catch (SQLException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (rs != null)
            {
                try
                {
                    rs.close();
                } catch (SQLException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

在处理方面:

while (true)
    {
        ResultData result = null;
        try
        {
            try
            {
                result = (ResultData) TrendApp.resultSetProcQueue.take();

            } catch (InterruptedException e)
            {
                errorLog.error("Unable to fetch message from Data Adapter processing queue.");
            }
} finally
        {
            if (result != null)
            {
                result.setData(null);
                result = null;
            }
        }

    }

这是结果集到ArrayList的转换

public List<HashMap<String, Object>> resultSetToArrayList(ResultSet rs) throws SQLException
{
    ResultSetMetaData md = rs.getMetaData();
    int columns = md.getColumnCount();
    List<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();

    while (rs.next())
    {
        HashMap<String, Object> row = new HashMap<String, Object>(columns);
        for (int i = 1; i <= columns; ++i)
        {
            row.put(md.getColumnName(i), rs.getObject(i));
        }
        list.add(row);
    }
    return list;
}

1 个答案:

答案 0 :(得分:0)

VM将尝试保留您的峰值大小。 Java可以在更多内存的情况下表现更好,并且在需要时可以很好地进行清理。

针对您的特定问题(限制总内存使用量),只需将-Xmx设置为您希望分配的最大值,而不是尝试手动清理变量。 Java将为您完成剩下的工作。尝试允许某种额外分配用于“峰值”使用然后强制退回它真的不是一个好主意。

你需要担心的是记忆力不断增长。探查器可以告诉您对象是否未被释放,但您可以通过调用System.gc()两次然后打印出已用内存量(总内存 - 可用内存,没有内存)来进行自己的迷你内存检查一个实际的“使用”)。如果在内存密集型进程的每次迭代后检查此数字,它不应该增长 - 它应该在特定值附近悬停。

获得准确内存大小所需的gc调用会缩小内存使用量,因此您不希望在生产中执行此操作。它将搞乱Java的整个内存分配/优化系统。