6

2 つの質問。

  1. リテラル文字列を宣言すると、ヒープの文字列プールに同じ文字列が存在するかどうかを検索します。これもインターン(クラスのメソッドインターンString)ですか?

  2. 私の考えでは、各リテラル文字列宣言にはバイナリ検索などが必要なので、 nがプール内の既存の文字列の数である場合、少なくともlog(n)のコストがかかります。また、プールに多くの文字列がある場合、コストが高くなる可能性があります。(検索コストとメモリのトレードオフでしょうか?) この観点からすると、mant リテラル文字列を宣言するのは危険かもしれません。 この検索コストの重要性と、Java がこのように設計されている理由 (リテラル文字列が宣言されている場合のプールの検索)。

以下は、背景を理解するために参照したものです。


クラスJavaDoc にはjava.lang.String次のように記載 されています。

文字列は定数です。作成後に値を変更することはできません。文字列バッファーは可変文字列をサポートします。String オブジェクトは不変であるため、共有できます。

http://www.janeg.ca/scjp/lang/strLiteral.htmlコメント:

つまり、コンパイラは文字列の元の値が一度作成されると変更できないことを認識しているため、既存のデータを安全に使用し、重複によるメモリの混乱を避けることができます。

4

2 に答える 2

4

1 - リテラル文字列を宣言するとき、ヒープの文字列プールに同じ文字列があるかどうかを検索します。これもインターニング(クラス文字列のメソッドインターン)ですか?

はい。このプロセスはインターンと呼ばれます。ただし、リテラルを含むクラスがロードされたときに一度だけ発生します。

2-私の考えでは、各リテラル文字列宣言にはバイナリ検索などが必要なので、n がプール内の既存の文字列の数である場合、少なくとも log(n) のコストがかかります。

いいえ、そうではありません。プールはハッシュ テーブルです。

...そして、プールに多くの文字列がある場合、コストが高くなる可能性があります。

いいえ、そうではありません。文字列プールのハッシュ テーブルでのルックアップのコストは ですO(1)

... この観点から、多くのリテラル文字列を宣言するのは危険かもしれません。

このコストは、クラス ファイルをロードしてから JIT コンパイルする他のコストと比較すると、それほど重要ではありません。多くのリテラル文字列を宣言する際に、パフォーマンスに関連する「危険」はありません。

明らかに、文字列リテラルに対応する String オブジェクトは「永続的に」メモリを占有するため、通常、不必要にメモリを浪費することは望ましくありません。しかし、これらの定数文字列を使用する必要がある場合は、何らかの方法で表現する必要があります。また、それらを表現する他の方法では、別の方法でメモリを使用するか、別のランタイム コストが必要になります。たとえば、ファイルからそれらを読み取ったり、データベースからそれらを取得したりするコスト。

文字列リテラルをインターンする利点は、同じリテラル文字列の複数のコピーでヒープが乱雑にならないことです。これは、一般的な SE / EE アプリケーションではおそらく重要ではありませんが、ME プラットフォームではヒープ メモリが貴重であり、無駄にするのはよくありません。


@RENO は、文字列がインターンされる回数について尋ねます。次の 2 つのケースがあります。

  • 明示的な呼び出しはString.intern()、アプリケーションが行うことを選択した回数 (または少ない回数) 発生します。

  • 文字列リテラルの場合、javacコンパイラは、特定の.classファイルの定数プールに文字列リテラルの複数のコピーが含まれていないことを確認します。これは、多くの場所に特定のリテラルを持つクラスは、クラスがロードされたときに一度だけリテラルがインターンされることを意味します。ただし、それぞれのソース コードに同じリテラル文字列を持つ 2 つのクラスがある場合、両方ともそれぞれの定数プールに文字列値があり、それぞれのクラスが読み込まれるときに両方とも文字列をインターンします。

于 2011-02-21T03:21:15.050 に答える
4

コンパイル時の複雑さと実行時の複雑さを混同しています。

クラスがロードされると、はい、各リテラルが既に存在するかどうかを確認するために検索を行います (ただし、提案の代わりに O(1) ルックアップにハッシュマップを使用すると思います)。

コードが実行されると、メモリ内の文字列への参照が保持されるため、非リテラル以外の追加コストはありません。

はい、リテラルはインターンされます。文字列の Javadoc によると、

最初は空である文字列のプールは、クラス String によってプライベートに維持されます。

String を呼び出しintern()て、このプールに追加できます。一意のプールから戻ることが保証されているため、論理的には if a.equals(b)thenに従います。a.intern() == b.intern().intern()

例:

class InternTest {
    // assuming InternTest is the only class, internPool.size = 0
    String x = "ABC"; // interned at class load, internPool.size = 1
    String y = "DEF"; // interned at class load, internPool.size = 2
    String z = "ABC"; // interned at class load, but match found - size = 2 still

    void foo() {
        // random int is just a mechanism to get something that I know won't
        // be interned at loadtime - could have loaded from file or database too
        int i = (new java.util.Random()).nextInt(1000) + 100;
        int j = i;
        String s = String.valueOf(i); // not yet interned, size = 2 still
        String t = String.valueOf(j); // not yet interned, size = 2 still

        String sIntern = s.intern(); // manually interned, size = 3 now
        String tIntern = t.intern(); // manually interned, match found, size = 3 still

        System.out.println("equals: " + (s.equals(t))); // should be true
        System.out.println("== raw: " + (s == t)); // should be false, different variables
        System.out.println("== int: " + (sIntern == tIntern)); // should be true, from unique pool

       System.out.println("x and z: " + (x == z)); // should be true, interned at class load
    }

    public static void main(String[] args) {
        (new InternTest()).foo();
    }

}

実行時の結果:

C:\Documents and Settings\glowcoder\My Documents>java InternTest
equals: true
== raw: false
== int: true
x and z: true

この仮定は決して真実ではないことを指摘しておきます。Java 言語自体には、私たちの言語が日の目を見るString前に抑留される多くの言語があります。Stringただし、すべてがシーケンシャルにロードされると仮定し、インターンされている Strings のデルタのみを考慮し、既存のインターンとの衝突がないと仮定すると (インターンがうるさく、ドラマに満ちていることは誰もが知っていますよね?スニッカー)、数字は実際に文字列プールのサイズのデルタ。

于 2011-02-21T02:07:43.233 に答える