2017-01-27 6 views
4

私の同僚と私はアプリケーションにいくつかのメモリ問題を抱えています。私たちが行ったディスカバリーの1つはデータベースから来たストリング値です(重複度が高い)は、実際には中止されていません。したがって重複した値がメモリに保持され、潜在的に大きな問題になる可能性があります。Java JDBCが文字列値を再利用していませんか?

たとえば、SQLiteデータベースと同じ文字列をクエリする単純なJDBCの例を次に示します。私はそれぞれのIDハッシュコードを表示し、それぞれが別のインスタンスであることを示します。

import java.sql.*; 

public class Test { 
    public static void main(String[] args) 
    { 
     Connection connection = null; 
     try 
     { 
      // create a database connection 
      connection = DriverManager.getConnection("jdbc:sqlite:/C:/rexon_metals.db"); 
      Statement statement = connection.createStatement(); 

      ResultSet rs = statement.executeQuery("SELECT REGION FROM CUSTOMER WHERE REGION = 'Southwest'"); 
      while(rs.next()) 
      { 
       String region = rs.getString("REGION"); 
       System.out.println(region + ": " + System.identityHashCode(region)); 
      } 
     } 
     catch(SQLException e) 
     { 
      // if the error message is "out of memory", 
      // it probably means no database file is found 
      System.err.println(e.getMessage()); 
     } 
     finally 
     { 
      try 
      { 
       if(connection != null) 
        connection.close(); 
      } 
      catch(SQLException e) 
      { 
       // connection close failed. 
       System.err.println(e); 
      } 
     } 
    } 
} 

OUTPUT:

Southwest: 405662939 
Southwest: 653305407 
Southwest: 1130478920 
Southwest: 1404928347 

しかし、私は明示的にString.intern()メソッドを呼び出した場合、すべてのアイデンティティーハッシュコードは同じです。

String region = rs.getString("REGION").intern(); 

OUTPUT:

Southwest: 405662939 
Southwest: 405662939 
Southwest: 405662939 
Southwest: 405662939 

なぜJDBCは私のためにintern()を呼び出すことはありませんでしょうか?これは、多くの重複したString値がある場合に、開発者が行うことが期待されますか?これは頻繁に使用され、アプリケーションのセッション全体で持続されますか?

P.S. - 何百にもなる数百万の文字列値があります。これは手動のintern()コールを保証するものですか?

+2

私はなぜあなたが驚いているのか分かりません。 '.intern()'がいつ役に立つかを判断するために、Javaランタイムが文字列を解析することを何も見たことがありません。特に、データベースクエリが呼び出されたときにこれを行うことができるので、確かに良い考えであると思われます。 – arcy

+0

私は実際にそれについて考えているので、なぜ私が驚いているのか分かりません。私はJVMが私にとって魔法の最適化を行うと思っていました。今では、永続性がどのように発生しなければならないかを実際に検討しているので、なぜそれが必ずしも自動的に起こらないのか分かります。 – tmn

+4

実際には、このような "魔法の"最適化があります:G1ガベージコレクタで使用できる '-XX:+ UseStringDeduplication'です。 – apangin

答えて

5

JDBCドライバは、データベースから取得する文字列データを受け入れません。

インターナショナルが比較的高価であることを考慮する必要があります。ドライバーは、特にJDBC結果セットがデータベースからビットごとにストリームされる可能性が高いため、どのデータが高度に反復的であるかを容易に予測できません。コードがそれを横断します。

メモリがメモリの大きなボトルネックである場合は、の文字列データは非常に反復性が高いため、手動でintern()することができます。これにより、文字列がドライバによって作成されるのを防ぐことはできないことに注意してください。唯一変化するのは、コピーが範囲外になるとすぐにガベージコレクション可能になることです。

データベースにこのような繰り返し文字列がある場合、データベースが正しく設計されているかどうかは確かにわかります最初にです。ストリングが実際に固定セットを表すことが判明した場合、それらをコード表に変えることを検討する。ただのID。

+0

ありがとう私はデータベース設計とIDの文字列を別のテーブルで正規化することについて不思議だった。効率的ではあるがデータベースはより難解なものになり、前者はビューによって緩和される可能性がある。私は、個々のレコードのオンデマンドではなく、包括的なセットとしてデータを引き出す必要がありますが、過剰なオブジェクトの作成を最小限に抑えるためには必要です。私はこの記事のすべてのアドバイスを考慮し、重視します。皆さんありがとう! – tmn

+0

これは、特定のドライバ固有のものであることを強調する価値があります。この場合sqlite。ほとんどのドライバーは(私の経験から)そうではありませんが、別のドライバーが文字列を正規化することができます( 'intern() 'を経由する必要はありません)。 – Holger

+0

Guavaには、https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Interners.html#newWeakInterner()もインターンシップするための効率的なユーティリティがあります。 – tmn

関連する問題