2

私のプログラムは十分に高速ですが、1人のユーザーの最大メモリ使用量が最大300 MBになり、アプリケーションを常にクラッシュさせる可能性のあるユーザーはほとんどいないため、メモリ最適化の速度をあきらめたいと思います。私が見つけた答えのほとんどは速度の最適化に関連しており、その他は一般的なものでした(「データベースからメモリに直接書き込む場合は、メモリの使用量はそれほど多くないはずです」)。ええと、あるようです:)誰かのアイデアを「ロック」しないように、コードを投稿しないことを考えていましたが、一方で、私がすでに行ったことを確認できない場合は、時間を無駄にする可能性がありますだからここにあります:

// 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で取得):

メモリ使用量のスクリーンショット

EDIT2:データベースはOracle10.2.0.4です。ResultSet.TYPE_FORWARD_ONLYを設定し、最大50MBの使用量を得ました。コメントで言ったように、これからは注目しますが、本当に有望です。

ResultSet.TYPE_FORWARD_ONLYを追加した後のメモリ使用量

EDIT3:利用可能な別の可能な最適化があるようです。私が言ったように、私はXMLを生成しています。つまり、大量のデータが繰り返されます(他に何もない場合はタグ)。つまり、String.intern()がここで役立つ可能性があります。これをテストするときに、投稿します。

4

3 に答える 3

3

ResultSet.TYPE_FORWARD_ONLYを使用することは可能ですか?

ResultSet.TYPE_SCROLL_INSENSITIVEを使用しました。一部のデータベース(どちらを使用するかは指定していません)では、これにより結果セット全体がメモリにロードされると思います。

于 2010-09-14T07:40:32.770 に答える
0

Java であるため、プログラム全体の寿命を持つシングルトンのメンバーであるリストに物事をプッシュする場合や、私の経験ではリソース リークの可能性が高い場合など、参照をリークしていない限り、メモリは一時的にのみ急増するはずです。これは、ファイル ハンドルなどのアンマネージ リソースを使用するオブジェクトがクリーンアップ コードを呼び出さない場合に発生します (これは、再スローされない空の例外ハンドラーによって一般的に引き起こされる状態です)。最終的にブロックを回避するという最終的な効果を持つ親スタックフレームに...

于 2010-09-14T07:24:14.967 に答える
0

さらにいくつかのテストを実行しましたが、結論は次のとおりです。

  1. 最大の利点は JVM にあります (または、visualvm では Java 5 ヒープ領域の監視に問題があります:)。ResultSet.TYPE_FORWARD_ONLY が大幅な利益をもたらしたと最初に報告したとき、私は間違っていました。最大の利点は、同じコードが最大 150 MB を占める Java 6 とは対照的に、同じプログラムが最大 50 MB のヒープスペースを使用する Java 5 を使用したことです。
  2. 2 番目の利点は ResultSet.TYPE_FORWARD_ONLY にあります。これにより、プログラムは可能な限り少量のメモリを使用するようになりました。
  3. 3 つ目の利点は Sting.intern() にあります。これにより、新しい文字列を作成する代わりに文字列をキャッシュするため、プログラムのメモリ使用量が少し少なくなります。

これは、最適化 2 および 3 での使用法です (String.intern() がなければ、グラフは同じになります。すべてのポイントに 5 MB だけ追加する必要があります)。

代替テキスト

そして、これはそれらを使用しない場合の使用法です(最後の使用量が少ないのは、プログラムがメモリ不足になるためです:)) 代替テキスト

ご協力いただきありがとうございます。

于 2010-09-15T08:26:22.950 に答える