2010-09-14 28 views
2

我的程序速度不够快,但我宁愿放弃内存优化的速度,因为一个用户的最大内存使用量可达300 MB,这意味着它们中的很少一些可能会不断地使应用程序崩溃。我发现的大部分答案都与速度优化有关,其他只是一般(“如果你直接从数据库写入内存,应该没有太多的内存使用量”)。好吧,似乎有:)我正在考虑不发布代码,所以我不会“锁定”某人的想法,但另一方面,如果你没有看到我已经完成的工作,我可能会浪费你的时间所以在这里,它是:在Java中将数据库写入(zip)文件的最有效的方法是什么?

// First I get the data from the database in a way that I think can't be more 
// optimized since i've done some testing and it seems to me that the problem 
// isn't in the RS and setting FetchSize and/or direction does not help. 

public static void generateAndWriteXML(String query, String oznaka, BufferedOutputStream bos, Connection conn) 
     throws Exception 
{ 
    ResultSet rs = null; 
    Statement stmt = null; 
    try 
    { 
     stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); 
     rs = stmt.executeQuery(query); 
     writeToZip(rs, oznaka, bos); 
    } finally 
    { 
     ConnectionManager.close(rs, stmt, conn); 
    } 
} 

// then I open up my streams. In the next method I'll generate an XML from the 
// ResultSet and I want that XML to be saved in an XML, but since its size takes up 
// to 300MB, I want it to be saved in a ZIP. I'm thinking that maybe by writing 
// first to file, then to zip I could get a slower but more efficient program. 

private static void writeToZip(ResultSet rs, String oznaka, BufferedOutputStream bos) 
     throws SAXException, SQLException, IOException 
{ 
    ZipEntry ze = new ZipEntry(oznaka + ".xml"); 
    ZipOutputStream zos = new ZipOutputStream(bos); 
    zos.putNextEntry(ze); 
    OutputStreamWriter writer = new OutputStreamWriter(zos, "UTF8"); 
    writeXMLToWriter(rs, writer); 
    try 
    { 
     writer.close(); 
    } catch (IOException e) 
    { 
    } 
    try 
    { 
     zos.closeEntry(); 
    } catch (IOException e) 
    { 
    } 
    try 
    { 
     zos.flush(); 
    } catch (IOException e) 
    { 
    } 
    try 
    { 
     bos.close(); 
    } catch (IOException e) 
    { 
    } 
} 

// And finally, the method that does the actual generating and writing. 
// This is the second point I think I could do the memory optimization since the 
// DataWriter is custom and it extends a custom XMLWriter that extends the standard 
// org.xml.sax.helpers.XMLFilterImpl I've tried with flushing at points in program, 
// but the memory that is occupied remains the same, it only takes longer. 

public static void writeXMLToWriter(ResultSet rs, Writer writer) throws SAXException, SQLException, IOException 
{ 
    //Set up XML 
    DataWriter w = new DataWriter(writer); 
    w.startDocument(); 
    w.setIndentStep(2); 
    w.startElement(startingXMLElement); 
    // Get the metadata 
    ResultSetMetaData meta = rs.getMetaData(); 
    int count = meta.getColumnCount(); 
    // Iterate over the set 
    while (rs.next()) 
    { 
     w.startElement(rowElement); 
     for (int i = 0; i < count; i++) 
     { 
      Object ob = rs.getObject(i + 1); 
      if (rs.wasNull()) 
      { 
       ob = null; 
      } 
      // XML elements are repeated so they could benefit from caching 
      String colName = meta.getColumnLabel(i + 1).intern(); 
      if (ob != null) 
      { 
       if (ob instanceof Timestamp) 
       { 
        w.dataElement(colName, Util.formatDate((Timestamp) ob, dateFormat)); 
       } 
       else if (ob instanceof BigDecimal) 
       { 
        // Possible benefit from writing ints as strings and interning them 
        w.dataElement(colName, Util.transformToHTML(new Integer(((BigDecimal) ob).intValue()))); 
       } 
       else 
       { // there's enough of data that's repeated to validate the use of interning 
        w.dataElement(colName, ob.toString().intern()); 
       } 

      } 
      else 
      { 
       w.emptyElement(colName); 
      } 
     } 
     w.endElement(rowElement); 
    } 
    w.endElement(startingXMLElement); 
    w.endDocument(); 
} 

编辑:这里是存储器的使用(与VisualVM的采取)的一个例子:

Memory usage screenshot

EDIT2:数据库是Oracle 10.2.0.4。并且我已经设置了ResultSet.TYPE_FORWARD_ONLY并获得了最多50MB的使用量!正如我在评论中所说的那样,我会密切关注这一点,但它确实很有希望。

Memory usage after adding ResultSet.TYPE_FORWARD_ONLY

EDIT3:似乎有可用另一种可能的优化。正如我所说,我正在生成一个XML,这意味着大量的数据会重复(如果没有其他的话,那么标签),这意味着String.intern()可以帮助我,我会在测试时回复。

+0

你能解决这个格式吗? – whiskeysierra 2010-09-14 07:21:52

+0

不知道你在什么时候看到它,当它被张贴它被打破,所以我redid它。如果还不够好,建议执行什么操作:) – Andrija 2010-09-14 07:24:18

+0

我没有读取所有代码,但我知道您正试图将一些关系数据转换为XML并将其存储在您的zip文件中。你能绕过java吗?一些RDBMS系统导出2 xml。然后你只需压缩文件。 – emory 2010-09-14 07:34:01

回答

0

我跑了一些测试和结论是:

  1. 最大的收获是在JVM(或VisualVM的有问题的监控Java 5的堆空间:)。当我第一次报告ResultSet.TYPE_FORWARD_ONLY让我获得重大收益时,我错了。最大的收益是使用Java 5,在该Java 5下相同的程序使用了高达50MB的heappace,而Java 6则使用相同的代码占用150 MB。
  2. 第二个增益在ResultSet.TYPE_FORWARD_ONLY中,它使程序占用尽可能少的内存。
  3. 第三个增益在Sting.intern()中,它使得程序的内存少一些,因为它缓存了字符串,而不是创建新的字符串。

这是优化2和3的用法(如果没有String。实习生()的图形将是相同的,你应该只多加上5 MB到每一个点)

alt text

,这是没有他们(该程序会使用在最后的较小的使用是由于内存不足:)) alt text

谢谢大家的帮助。

0

因为它是Java,所以内存只应该暂时加速,除非你泄漏了引用,就像你把东西推到一个拥有整个程序寿命的单身人士的列表上一样,或者以我的经验更多可能是资源泄漏,这发生在(当然,我假设适用于Java,尽管我在考虑C#)使用非托管资源(如文件句柄)的对象从不调用它们的清理代码,这通常是由空的异常处理程序造成的不会重新抛出父堆栈框架,它具有规避最终阻止的净效果...

+0

它的确出现了尖峰。我将使用内存使用情况分析的屏幕快照编辑OP。 – Andrija 2010-09-14 07:32:01

+0

这是一个猜测,但也许java压缩类需要有内存中的全部源来产生它的输出,它可以绕过streamwriter的节省? – 2010-09-14 07:36:32

+0

可能,这就是我在这里问的原因:) – Andrija 2010-09-14 09:24:00

3

是否可以使用ResultSet.TYPE_FORWARD_ONLY?

您已经使用了ResultSet.TYPE_SCROLL_INSENSITIVE。我相信对于某些数据库(你没有说你使用哪一个)会导致整个结果集被加载到内存中。

+0

我们使用的是Oracle 10.2.0.4。我已经使用了这个,我得到了结果!我仍然怀疑他们(意味着我会做更多的测试/分析),但现在看起来确实很有希望。新的内存使用情况包含在OP中。 – Andrija 2010-09-14 07:53:01

+0

是的,请查看http://download.oracle.com/docs/cd/B19306_01/java.102/b14355/resltset.htm#CIHCHBJB。一般来说,使用除ResultSet.TYPE_FORWARD_ONLY之外的任何东西都不是一个好主意。 – gpeche 2010-09-14 07:58:44

相关问题