2012-07-27 70 views
1

我正在开发一个运行在Tomcat 6上的Java项目,它连接到一个MySQL数据库。所有程序都按照他们的要求运行,无论是在本地测试客户服务器上的测试时。然而,有一个例外,那就是一个检索大量数据来生成报告的过程。存储过程在从MySQL执行时需要13分钟左右的时间。当我在本地运行应用程序并连接到在线数据库时,该过程确实起作用,唯一不起作用的是它在客户端的服务器上运行。Java storedProcedure停止与OutOfMemoryError

客户端对他的服务器非常有保护,所以我们对它的控制有限,但他们确实希望我们解决问题。当我检查日志文件时,从执行存储过程的函数中不会引发错误。并且在代码中放置一些调试日志,它会显示它确实进入了执行调用,但在调用之后不会记录调试,也不会将错误记录到catch中,而是进入finally部分。

他们声称MySQL日志中没有超时错误。

如果任何人有任何想法可能会导致此问题,任何帮助将不胜感激。

更新

经过一番唠叨服务器管理员,我终于得到了进入卡塔利娜日志,这些日志中,我终于发现,有一些意义的错误:

Exception in thread "Thread-16" java.lang.OutOfMemoryError: Java heap space 
     at java.util.Arrays.copyOf(Arrays.java:2894) 
     at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:117) 
     at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:407) 
     at java.lang.StringBuffer.append(StringBuffer.java:241) 
     at be.playlane.mink.database.SelectExportDataProcedure.bufferField(SelectExportDataProcedure.java:68) 
     at be.playlane.mink.database.SelectExportDataProcedure.extractData(SelectExportDataProcedure.java:54) 
     at org.springframework.jdbc.core.JdbcTemplate.processResultSet(JdbcTemplate.java:1033) 
     at org.springframework.jdbc.core.JdbcTemplate.extractReturnedResultSets(JdbcTemplate.java:947) 
     at org.springframework.jdbc.core.JdbcTemplate$5.doInCallableStatement(JdbcTemplate.java:918) 
     at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:876) 
     at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:908) 
     at org.springframework.jdbc.object.StoredProcedure.execute(StoredProcedure.java:113) 
     at be.playlane.mink.database.SelectExportDataProcedure.execute(SelectExportDataProcedure.java:29) 
     at be.playlane.mink.service.impl.DefaultExportService$ExportDataRunnable.run(DefaultExportService.java:82) 
     at java.lang.Thread.run(Thread.java:636) 

奇怪的是,这不记录到应用程序日志,即使它被包裹在try catch中。现在,基于该错误,问题就出withing这个方法:

public Object extractData(ResultSet rs) throws SQLException, DataAccessException 
    { 
    StringBuffer buffer = new StringBuffer(); 

    try 
    { 
     // get result set meta data 
     ResultSetMetaData meta = rs.getMetaData(); 
     int count = meta.getColumnCount(); 

     // get the column names; column indices start from 1 
     for (int i = 1; i < count + 1; ++i) 
     { 

     String name = meta.getColumnName(i); 
     bufferField(name, i == count, buffer); 
     } 

     while (rs.next()) 
     { 

     // get the column values; column indices start from 1 
     for (int i = 1; i < count + 1; ++i) 
     { 
      String value = rs.getString(i); 
      bufferField(value, i == count, buffer); 
     } 
     } 
    } 
    catch (Exception e) 
    { 
     logger.error("Failed to extractData SelectExportDataProcedue: ", e); 
    } 

    return buffer.toString(); 
    } 

    private void bufferField(String field, boolean last, StringBuffer buffer) 
    { 
    try 
    { 
     if (field != null) 
     { 

     field = field.replace('\r', ' '); 
     field = field.replace('\n', ' '); 

     buffer.append(field); 
     } 

     if (last) 
     { 
     buffer.append('\n'); 
     } 
     else 
     { 
     buffer.append('\t'); 
     } 
    } 
    catch (Exception e) 
    { 
     logger.error("Failed to bufferField SelectExportDataProcedue: ", e); 
    } 
    } 

这些功能的目标是到一定的结果集导出到Excel文件(这发生在一个较高的水平)。

所以,如果任何人有一些优化这个技巧,他们非常欢迎。

+1

哎哟......没有服务器访问权限,你会遇到这个问题。你可以做一个修改后的存储过程的缩短子集:例如:快速运行并通过测试环境运行的TOP 100?或者向应用程序添加一些日志记录,并通过日志进行调试,也许你的应用程序正在吃掉异常。 – 2012-07-27 15:28:15

+0

正如你可以在更新中看到的,我终于掌握了包含有意义错误日志的catalina日志。所以我已经设法找到问题了,但是,我不确定如何优化这个。 – Andy 2012-07-30 08:15:54

+0

你无法理解这一点的原因是它是'错误'而不是'Exception'! (不,添加'catch(Error e)'是**不是**正确的解决方案)另外:你建立一个巨大的'String',这可能是问题的根源,你应该尝试将数据流到任何地方你想要它而不是为它分配一个巨大的对象。 – 2012-07-30 08:18:48

回答

2

好了,你的堆栈跟踪为您提供了答案:

Exception in thread "Thread-16" java.lang.OutOfMemoryError: Java heap space 

这就是为什么你没有登录,应用程序崩溃(螺纹,具体)。从你的描述来看,这听起来像你有一个需要分页的海量数据集。

 while (rs.next()) 
     { 

     // get the column values; column indices start from 1 
     for (int i = 1; i < count + 1; ++i) 
     { 
      String value = rs.getString(i); 
      bufferField(value, i == count, buffer); 
     } 
     } 

这是你的线程死亡(可能)。基本上你的StringBuffer内存不足。至于纠正它,有很多选择。在客户端上的问题投入更多的内存(可以通过配置JVM(这里有一个链接): How to set the maximum memory usage for JVM?

或者,如果你已经这样做,投入更多的RAM到设备

从。从编程的角度来看,这听起来像是一个报告,你可以将一些数据转移到MySQL而不是缓冲到最后(如果可能的话),或者如果这是一个巨大的报告,我会考虑将它流式传输到文件然后通过缓冲流读取以填充报告

这完全取决于报告的内容如果它很小,我的目标是在SQL中做更多的工作以便mi最小化结果集。如果这是一个巨大的报告,那么缓冲是另一种选择。

您可能会丢失的另一种可能性是ResultSet(取决于实现)可能被缓冲。这意味着不是将所有内容全部读取到字符串,也许您的报告可以直接获取ResultSet对象并从中进行打印。当然,这个缺点是,一个流浪的SQL异常会杀死你的报告。

祝您好运,我会先尝试一下内存选项。你可能会用像128这样热闹的小东西来运行,这很简单(我在远程管理的机器上看到过这种情况)。

+0

对不起,对于最近的回复,我已经准备好了想通了myselve将它流式传输到一个文件是这项任务的最佳方法。谢谢你的回答。 – Andy 2012-09-03 09:22:19