JVM堆不断增加。为什么?

时间:2018-02-22 13:40:35

标签: java jdbc memory-leaks resources try-with-resources

executor = Executors.newScheduledThreadPool(1);
Runnable helloRunnable = new Runnable() {
    public void run() {
        controller2.GetAIntFromDatabase(columblName);
    }
};
executor.scheduleAtFixedRate(helloRunnable, 0, 10, TimeUnit.MILLISECONDS);

程序的这一部分会产生不断增加的堆内存错误。

 controller2.GetAIntFromDatabase(columblName); 

使用此函数我从数据库中读取一个int值。

    @Override
    public int GetAIntFromDatabase(String columblName) {
        int stare = 0;
        try{
            String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1"; 
            PreparedStatement preparedStatement = this.connnection.prepareStatement(query);          
            ResultSet resultSet = preparedStatement.executeQuery();
            if (resultSet.next()){
               stare = resultSet.getInt(columblName);
             preparedStatement.close();            
                resultSet.close();
               return stare;
            }
            preparedStatement.close();            
            resultSet.close();
        }catch (SQLException ex) {
            System.out.println("GetUtilajStare Error: "  + ex);
            return 0;
        }

        return 0;
    }

运行10分钟后的Java堆内存使用情况:

used Java Heap memory for 10 min

为什么我的堆内存不断增加?

2 个答案:

答案 0 :(得分:2)

如果在打开preparedStatementresultSet后抛出异常,则永远不会关闭它们。因此,您应该使用始终执行的finally块。

public int GetAIntFromDatabase(String columblName) {
    int stare = 0;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try {
        String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
        preparedStatement = this.connnection.prepareStatement(query);
        resultSet = preparedStatement.executeQuery();
        if (resultSet.next()) {
            stare = resultSet.getInt(columblName);
            return stare;
        }
    } catch (SQLException ex) {
        System.out.println("GetUtilajStare Error: " + ex);
        return 0;
    } finally {
        if (preparedStatement != null)
            preparedStatement.close();
        if (resultSet != null) 
            resultSet.close();
    }
    return 0;
}

答案 1 :(得分:0)

您应该使用Java 7+关闭资源的方式try-with-resources

  

try-with-resources语句是一个声明一个或多个资源的try语句。资源是在程序完成后必须关闭的对象。 try-with-resources语句确保在语句结束时关闭每个资源。任何实现AutoCloseable的对象(包括实现Closeable的所有对象)都可以用作资源。

public int GetAIntFromDatabase(String columblName) {
    final String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
    try (final PreparedStatement preparedStatement = this.connnection.prepareStatement(query)) {
        final ResultSet resultSet = preparedStatement.executeQuery();
        if (resultSet.next()) {
            return resultSet.getInt(columblName);
        }
        return 0;
    } catch (SQLException ex) {
        // Do something better than System.out.println(...)
        return 0;
    } 
    return 0;
}

此外,您不必显式关闭结果集,准备好的语句按owns the result set执行:

  

Statement对象关闭时,其当前ResultSet对象(如果存在)也将关闭。

然而,如果你想要偏执并且过度杀戮,正如@MarkRotteveel在他的评论中建议的那样,你可以添加AutoCloseable资源ResultSet,然后代码看起来像这样:

public int GetAIntFromDatabase(String columblName) {
    final String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
    try (final PreparedStatement preparedStatement = this.connnection.prepareStatement(query);
         final ResultSet resultSet = preparedStatement.executeQuery()
    ) {
    ...
}

我从来没有做过,也从不需要它,文档明确指出它不是不是,但有些人在某些边缘情况下遇到了问题。

在第二种情况下,特别可见的是资源试用可以节省多少 - 您无需将变量分配给null,您无需检查是否有任何变量它们已被打开,即使其中一个close()方法抛出异常,"关闭链"没有被破坏,并且在任何情况下都会调用其他资源的close()方法。