Java文字列の最長の長さとメモリサイズを取ります

一 序


ストリングは、理論的に最大の長さは、int型のメモリの最大値では、Integer.MAX_VALUE、
文字列リテラルテーブルの最大長はCONSTANT_Utf8_info、典型的には65535決定されます。

IIプレゼント


図1は、内部文字列実装
次のように、文字配列によって、その名声を文字のシーケンスを維持します。

最終char値プライベート[];
2
したがって、最大の長さは、他の配列の文字長のみバイト、文字、短いため、これはまた、最大の長さを示しているので、int型は、長くすることができない、文字列の文字列の最大長さに依存します一方で、我々は、Stringクラスがint型の変数は、次の文の方法、str.length()の戻り値を持って知っています:

int型の長さのパブリック()
。3
、このようにも理論の最大の長さを説明するが、実際には、以下の理論値よりも、

パブリッククラスmainClass {パブリック静的無効メイン(文字列[] args){
        // TODO自動生成方法スタブ
        のchar []値=新しいCHAR [Integer.MAX_VALUEで]。   
        System.out.println( "");
    }

}

システムは、大きなメモリ空間を割り当てることができないので、このエラーは、メモリのオーバーフローエラーです。
質問は今、コンピュータ・システムは、多くのメモリを割り当てることができ、ありますか?
 

三の分析 - ソース

 

java.lang.String.java

最終的なクラスStringの公共
    実装したjava.io.Serializable、同等の<文字列>、{たCharSequence
    / ** *値の文字のISに使用ストレージ/。
    民間最終char値[];
JavaのStringクラスのcharへ[]配列に格納する文字要素、従って、実際にはStringクラスの最大長は、[チャーに依存]配列は、配列の長さが可能である含んでいます。
我々は、単にMAX_LENGTHの文字[]配列の最大長であるかどうかを確認するために次の試験を行うことができます。
我々は320 339 961 lenの値を転送する場合、システムちょうど右のエラー、


したがって、文字[]配列は、320 339 960の最大長さに達することがあり、約2 ^ 28.255、各文字のバイト、すなわち、2 ^ 28.255バイト、及びGの4バイトのための空間が2 ^ 30に等しいです。

したがってチャー[]配列は、最大長さ(未満)4 Gにほぼ等しいです

文字列型320 339 960長さ、これ以上4以下G.の最大容量

 

 

文字列が内部char配列として格納され、配列の長さは、ストリングにInteger.MAX_VALUEにより許容される最大長である、int型です。また、文字を使用すると、最大の長さを格納するメモリの文字列4ギガバイトを必要とするので、おそらく、Javaの16ビットのメモリからです。しかし、このような「ABC」、「1a2b」などの場合、文字列リテラル(文字列リテラル)、これは、唯一の文字変数であるとリテラルのコード列で記述されたように、その後、最大許容文字列の長さが異なり書式文字列定数プールのストレージサイズのクラスファイル形式で格納されています。

CONSTANT_Utf8_info {
        U1タグ。
        U2の長さ;
        U1は、[長さ]バイト。
}


    U2は、文字列リテラルは、このように理論​​的に可能な最大長は65535 = 2 ^ 16-1であり、符号なし16ビット整数です。しかし、実際のテストは、最大長が許可することを示すだけ65534

分析の4つの異なるオペレーティング・ステージ

 

コンパイル

まず、私たちはコードのString =を使用したときの「について合理的な推論をみましょう」。時間の形式を定義するためのStringオブジェクト、「」そこの文字数制限をそこに?
それは合理的推論であるため、それは我々がString源から開始することができるように、十分な根拠でなければならない、公共文字列に従って(char値[]、 INTは、整数カウントをオフセット) に定義され、カウントは、したがって、文字、int型であります値は[]あなたはInteger.MAX_VALUEで、つまり2147483647文字まで保存することができます。(jdk1.8.0_73は)
しかし、実験は""、のString =を示し;、あなたは65,534文字まで持つことができます。あなたは、この数を超えた場合。これは、コンパイル時にエラーになります。

public static void main(String[] args) {

    String s = "a...a";// 共65534个a
    System.out.println(s.length());

    String s1 = "a...a";// 共65535个a
    System.out.println(s1.length());
}

 

上記のコードは、文字列になり、S1 =「...」; //コンパイルで65535の合計が失敗しました:

✗ javac StringLenghDemo.java
StringLenghDemo.java:11: 错误: 常量字符串过长

 

これは、65535の文字はそれをコンパイルすることができませんなぜ良い長さの制限は、2147483647であることは明らかですか?

私たちは、文字列リテラルは、直接文字列を定義し使用する場合、文字列は、ストレージの一定のプールになります。65534次に、上記事実は、定数プールを制限することです。
定数プール内の各データ項目は、独自の型を持ちます。JavaのCONSTANT_Utf8型のUnicode文字列のUTF-8エンコーディングは、一定のプールに示さ。
CONSTANTUtf8info CONSTANTUtf8タイプは、それが一定の文字列が格納されている、定数プールデータ項目です。すべてのリテラル定数プールは、ほとんどの場合CONSTANTUtf8infoによって記述されています。次のようにCONSTANTUtf8_infoが定義されて:

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

 

この記事の焦点はCONSTANTUtf8info紹介ではありませんので、ここでは詳細に始める、と私たちは私たちの文字列リテラルはCONSTANTUtf8infoを使用して保存されているクラスファイルで定義され、そしてCONSTANTUtf8infoはU2の長さを持って使用する必要はありません。型を示します格納されたデータの長さ。
U2は、符号なし16ビット整数、最大許容長は、理論的に2 ^ 16 = 65536です。Javaクラスファイルは、このように65536から2 = 65534バイトを残して、表現するために2つのバイトを使用して、null値を持つ店舗文字バリアントにUTF-8形式です。
この点で、クラスファイル形式の仕様にも明記されています。

フィールドとメソッド名、フィールドおよびメソッド記述子、および他の一定の文字列値の長さはCONSTANTUtf8info構造(§4.4.7)の16ビットの符号なし長項目によって65535文字に制限されます。限界は、符号化のバイト数ではなく、エンコードされた文字の数であることに留意されたいです。UTF-8は、2つのまたは3つのバイトを使用していくつかの文字をエンコードします。このように、マルチバイト文字を取り入れた文字列がさらに制限されています。

これはJavaで、すべてのデータのニーズはもちろん、文字列の定義が含ま定数プール、最大長が65535を超えることはできません、に保存する、と言うことです。

ランタイム

文字列これは、文字列S =「」を使用されるコンパイラの上記制限の長さを制限する。この定義は、いくつかの制限となりリテラル方法を。
まあ。実行時に文字列は制限がなく、答えにInteger.MAX_VALUE我々は前に挙げることことであり、この値は、文字列の長さがこの範囲を超えた場合、それは例外をスローすることが、実行時に、4Gにほぼ等しいです。(JDK前1.9)
intは32ビット変数タイプは、ある単語をカウントする正の数の部分をとり、それらが最大であることができます

2^31-1 =2147483647 个 16-bit Unicodecharacter

2147483647 * 16 = 34359738352 位
34359738352 / 8 = 4294967294 (Byte)
4294967294 / 1024 = 4194303.998046875 (KB)
4194303.998046875 / 1024 = 4095.9999980926513671875 (MB)
4095.9999980926513671875 / 1024 = 3.99999999813735485076904296875 (GB)

5のメモリ空間 

1、最初のStringオブジェクトのメモリフットプリントを教えて

次のように一般に、Java仮想マシン構成内のオブジェクトはある:
•オブジェクトヘッダ(オブジェクトヘッダ):8バイト(情報オブジェクトクラス、ID、仮想マシンの状態を保存するため)
•Javaの基本データ型:intなど、フロート、チャーおよび他のタイプのデータ
•参照(リファレンス):4バイト
•パディング(パディング)

文字列の定義:

JDK6:
民間最終char値[]。
民間最終int型のオフセット。
民間最終int型の数。
プライベートint型のハッシュ。

空の文字列によって占めJDK6空間は40のバイトです

JDK7:
民間最終char値[]。
プライベートint型のハッシュ。
プライベート過渡int型hash32。

空の文字列JDK7の占有スペースは40のバイトです

JDK6列のメモリフットプリント計算値:
空の文字列にスペースを占有最初の計算だけでなく、Javaでオブジェクトの配列を、したがってオブジェクトヘッダの配列があり、それは空間である空間のアレイによって占められるオブジェクトヘッドプラスアレイによって占め長さは、充填後に、すなわち、8 + 4 = 12バイト、16バイト。

空の文字列は、スペースを占領しました:

オブジェクトヘッダ(8バイト)+ char配列(16バイト)3 + INT(3×4 = 12バイト)+1 char配列参照(4バイト)= 40バイト。

そのため、実際の文字列のスペースを計算するための式は次のようで占められて:

8 *((+ 2 8 + 12×N + 4 + 12)+7)/ 8 = 8×(INT)((((N)* 2)+43)/ 8)

式中、n文字列の長さです。

2、例えば:

A、substringA

package demo;

輸入java.io.BufferedReader;

インポートのjava.io.File;

輸入java.io.FileInputStream;

輸入java.io.InputStreamReader;

パブリッククラスTestBigString

{

    プライベート文字列strsub。

    プライベート文字列strempty =新しいString();

    公共の静的な無効メイン(文字列[] args)が例外をスローします

    {

        TestBigString OBJ =新しいTestBigString();

        。obj.strsub = obj.readString()サブストリング(0,1)。

        糸。スリープ(* 1000 30 * 60)。

    }

    プライベート文字列readString()例外がスローされます

    {

        BufferedReaderのビス= NULL;

        試します

        {

            ビス=新しいをBufferedReader(新しいInputStreamReaderの(新しいFileInputStreamを(新しいファイル( "D:\\ teststring.txt"))));

            StringBuilderのSB =新しいStringBuilderの();

            文字列の行= NULL;

            しばらく((ライン= bis.readLine())!= NULL)

            {

                sb.append(ライン)。

            }

            システム。アウト .println(sb.length());

            リターンsb.toString();

        }

        最後に

        {

            (ビス!= null)の場合

            {

                )(bis.close。

            }

        }

    }

}

どこにファイル「D:\\ teststring.txt」33475740の文字、ファイルサイズ35Mがあります。

JDK6の使用は上記のコードを実行するために、あなたは、1つだけ取るstrsubちょうどサブストリング(0,1)を参照してください本当に一つだけをカウントしますが、メモリの量は、ほぼ67Mと高いようだったことができます。

 

しかし、JDK7コード前述と同様の動作が、strsubオブジェクトは、わずか40バイトであります

 

Bは、何がそれの原因は?

JDKのソースコードを見てください:

JDK6:

パブリックストリングサブストリング(INT beginIndexパラメータ、INT endIndexの){

    IF(beginIndexパラメータ<0){

        新しいStringIndexOutOfBoundsExceptionを(beginIndexのを)投げます。

    }

    もし(endIndexの>数){

        新しいStringIndexOutOfBoundsExceptionを(endIndexのを)投げます。

    }

    IF(beginIndexパラメータ> endIndexの){

        新しいStringIndexOutOfBoundsExceptionを(endIndexの - beginIndexパラメータ)を投げます。

    }

    リターン((beginIndexの== 0)&&(endIndexの==数))?この :

        新しいString(オフセット+ beginIndexの、endIndexに - beginIndexの、値);

}

スピードの値の配列を共有する//パッケージプライベートコンストラクタ。

    ストリング(INTオフセット、INTカウント、char値[]){

    this.value値を=。

    this.offset =オフセット。

    this.count =数えます。

}

JDK7:

パブリックストリングサブストリング(INT beginIndexパラメータ、INT endIndexの){

        IF(beginIndexパラメータ<0){

            新しいStringIndexOutOfBoundsExceptionを(beginIndexのを)投げます。

        }

        IF(endIndexの> value.length){

            新しいStringIndexOutOfBoundsExceptionを(endIndexのを)投げます。

        }

        int型subLen = endIndexの - beginIndexの。

        IF(subLen <0){

            新しいStringIndexOutOfBoundsExceptionを(subLen)を投げます。

        }

        リターン((beginIndexの== 0)&&(endIndexの== value.length))?この

                :新しい文字列(値、beginIndexの、subLen)。

}

パブリック文字列(char値[]、INTオフセット、INTカウント){

        IF(オフセット<0){

            新しいStringIndexOutOfBoundsExceptionを(オフセット)を投げます。

        }

        IF(カウント<0){

            新しいStringIndexOutOfBoundsExceptionを(カウント)を投げます。

        }

        //注:オフセットまたはカウントが近いかもしれません-1 >>> 1。

        { - (カウントオフセット> value.length)の場合

            新しいStringIndexOutOfBoundsExceptionを(オフセット+カウント)投げます。

        }

        this.value =配列。copyOfRange(値、オフセット、オフセット+カウント数)。

    }

元の文字列が予期せずにメモリ消費の多量に得られ、解放することができないので、JDK6は、元が原因String.substringである見ることができる()、文字列がまだ元への参照を保持返さ。

この設計の目的は、これらは、元の文字列の文字列と多重化されているので、メモリを節約するために、実際にJDK6ですが、int型のoffersetによって、サブストリングを識別するために、新しいStringと同等のものを数えます。

しかしながら、上記の例のために、以下で使用される巨大な文字列の数が少ないから採取し、この設計は、冗長データを大量に起因します。次のように文字列のstring.Split()またはString.substring()によって撮影された動作に関するこのような結論:

•大きなテキストの小さな文字列から取られたアプリケーションでは、String.substring()は、メモリの過度の浪費につながります。
•メモリを節約の目的を達成するように、正確に元のテキストに設計されたオリジナルのテキスト長以下から取られた一般的なテキスト内の文字列の数を、従来のString.substring()から取得した文字列の長さの合計が共有することができます。

メモリフットプリントの多くの根本原因への道は、()String.substringある結果は、元の文字列の数が含まれて返すので、その後、廃棄物のメモリの減少は、これらの元の文字列を削除することです。再び文字列から取り出した文字列のみを含む構造をNEWSTRINGコールは、String.toCharArray()メソッド呼び出すことができます。

文字列NEWSTRINGは、新しいStringを=(smallString.toCharArray());

Cは、同様に、を見てみましょうスプリット方式

パブリッククラスTestBigString

{

    プライベート文字列strsub。

    プライベート文字列strempty =新しいString();

    プライベート文字列[] strSplit。

    公共の静的な無効メイン(文字列[] args)が例外をスローします

    {

        TestBigString OBJ =新しいTestBigString();

        。obj.strsub = obj.readString()サブストリング(0,1)。

        obj.strSplit = obj.readString()スプリット( "アドレス:"、5);。

        糸。スリープ(* 1000年30 * 60)。

    }

JDK6アレイは列に分割、各メモリのメモリサイズは、元の文字列(67M)の文字列要素が占有されています。

 

JDK7ストリング及びアレイに分割され、各要素は、実際のメモリの文字列のサイズです。

 

D、の理由:

JDK6出典:

パブリック文字列[]スプリット(文字列の正規表現、INT限界){

    パターンを返します。コンパイル(正規表現).split(この限界)。

    }

パブリック文字列[]スプリット(たCharSequence入力、INT限界){

        int型のインデックス= 0;

        ブールmatchLimited =リミット> 0。

        ArrayListの<文字列>一致リスト=新しいのArrayList <文字列>();

        整合M =マッチャー(入力)。

        //各マッチ見つける前セグメントを追加します。

        一方、(m.find()){

            (もし!matchLimited || matchList.size()<リミット - 1){

                文字列一致= input.subSequence(インデックス、m.start())のtoString()。

                matchList.add(一致)。

パブリックたCharSequenceさらに、subSequence(INT beginIndexパラメータ、INT endIndexの){

        リターンthis.substring(beginIndexの、endIndexの);

    }

4.他の側面:

1、String a1 = “Hello”; //常量字符串,JVM默认都已经intern到常量池了。
创建字符串时 JVM 会查看内部的缓存池是否已有相同的字符串存在:如果有,则不再使用构造函数构造一个新的字符串,
直接返回已有的字符串实例;若不存在,则分配新的内存给新创建的字符串。
String a2 = new String(“Hello”); //每次都创建全新的字符串

図2に示すように、スプライシング静的な文字列は、通常、コンパイラは、この最適化を行いますので、+を使用してみてください。

公共の  文字列constractStr()

    {

        リターン  "STR1" + "STR2" + "STR3"。

}

バイトコードに対応します:

コード:

0:LDCの#24; //文字列str1str2str3 - スタックに文字列定数

2 areturn

3、スプライシング動的な文字列は、これ(のjavacコンパイラが自動的に最適化された接続文字列を行います)を構築過度の一時的なStringオブジェクトを減らし、APPENDのStringBufferあるいはStringBuilderのを使用しよう:

公共の  文字列constractStr(文字列str1は、文字列str2を、文字列STR3)

    {

        リターン  STR1 + str2の+ STR3。

}

バイトコードに対応する(StringBuilder.append JDK1.5メソッドを呼び出すために変換した後)。

コード:

0:新しい#24; //クラスのjava / LANG / StringBuilderを

3:後

4:aload_1

5:invokestatic#26; //メソッドJAVA / LANG / String.valueOf:(Ljava / LANG / OBJEC

T;)Ljava /ラング/文字列;

8:invokespecial#32。//メソッドのjava / LANG / StringBuilderの。 "の<init>" :( Ljava /ラ

/文字の;)V

11:aload_2

12:INVOKEVIRTUAL#35; //メソッドのjava / LANG / StringBuilder.append:(Ljava / LANG

/文字列;)Ljava /ラング/のStringBuilder。

15:aload_3

16:INVOKEVIRTUAL#35; //メソッドのjava / LANG / StringBuilder.append:(Ljava / LANG

/文字列;)Ljava /ラング/のStringBuilder; - のStringBuilderのappendメソッド呼出し

19:INVOKEVIRTUAL#39; //メソッドのjava / LANG / StringBuilder.toString :()Ljava /リットル

/文字列;

22:areturn - 戻る参照

 

リマーク 

自動ボクシングとアンボクシング

https://www.cnblogs.com/wang-yaz/p/8516151.html

https://blog.csdn.net/wolfking0608/article/details/78583944

https://www.iteye.com/blog/lin-yp-168367

公開された43元の記事 ウォン称賛28 ビュー40000 +

おすすめ

転載: blog.csdn.net/u013380694/article/details/102739636