6

かなり飽和状態のトピックの詳細について質問があるのではないかと思います。よく検索しましたが、その特定の明白な-非常に重要な問題に対する明確な答えを見つけることができませんでした。

UTF-8を使用してbyte[]をStringに変換する場合、各バイト(8ビット)はUTF-8でエンコードされた8ビット文字になりますが、各UTF-8文字はjavaでは16ビット文字として保存されます。あれは正しいですか?はいの場合、これは、各愚かなJava文字が最初の8ビットのみを使用し、メモリを2倍消費することを意味しますか?それも正しいですか?この無駄な行動はどのように受け入れられるのだろうか。

8ビットの疑似文字列を作成するためのトリックはありませんか?それは実際にメモリ消費量を減らすことになりますか?または、このメモリの浪費を回避するために、1つのJava 16ビット文字に2つ以上の8ビット文字を格納する方法はありますか?

紛らわしい答えをありがとう...

編集:こんにちは、答えてくれてありがとう。UTF-8の可変長プロパティを知っていました。しかし、私のソースは8ビットのバイトであるため、8ビットのUTF-8ワードしか必要ないことを(明らかに間違って)理解しました。UTF-8変換は、CLIで「catsomebinary」を実行したときに表示される奇妙な記号を実際に保存していますか?UTF-8は、バイトの可能な8ビットワードのそれぞれをUTF-8の特定の8ビットワードにマップするために何らかの形で使用されていると思いました。間違い?Base64を使用することを考えましたが、7ビットしか使用しないので悪いです。

再定式化された質問:バイトを文字列に変換するよりスマートな方法はありますか?byte[]をchar[]にキャストするのが好きかもしれませんが、それでも16ビットワードがあります。

追加のユースケース情報:

Jedis (NoSQL RedisのJavaクライアント)をhypergraphDBの「プリミティブストレージレイヤー」として採用しています。つまり、jedisは別の「データベース」のデータベースです。私の問題は、常にbyte []データをjedisにフィードする必要があることですが、内部的には、> Redis <(実際のサーバー)は「バイナリセーフ」文字列のみを処理しています。RedisはCで記述されているため、charは8ビット長であり、AFAIKは7ビットのASCIIIではありません。ただし、JedisのJavaの世界では、すべての文字は内部で16ビット長です。私はこのコードを(まだ)理解していませんが、jedisがこのjava 16ビット文字列をRedis準拠の8ビット文字列に変換すると思います(([ここ] [3])。これはFilterOutputStreamを拡張すると言っています。バイパスすることを望んでいます。 byte [] <->文字列変換をすべて行い、そのFilteroutputstreamを使用します...?)

今、私は疑問に思います:バイト[]と文字列を常に相互変換する必要があり、データサイズが非常に小さいものから潜在的に非常に大きいものまである場合、Java内で各8ビット文字を16ビットとして渡すためのメモリの大きな浪費はありませんか? ?

4

7 に答える 7

9

8ビットの疑似文字列を持つためのトリックはありませんか?

はい、Java のバージョンが最新であることを確認してください。;)

http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

-XX:+UseCompressedStrings 純粋な ASCII として表現できる文字列に byte[] を使用します。(Java 6 Update 21 パフォーマンスリリースで導入)

編集: このオプションは Java 6 update 22 では機能せず、Java 6 update 24 ではデ​​フォルトではオンになっていません。注: このオプションはパフォーマンスを約 10% 低下させる可能性があるようです。

次のプログラム

public static void main(String... args) throws IOException {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10000; i++)
        sb.append(i);

    for (int j = 0; j < 10; j++)
        test(sb, j >= 2);
}

private static void test(StringBuilder sb, boolean print) {
    List<String> strings = new ArrayList<String>();
    forceGC();
    long free = Runtime.getRuntime().freeMemory();

    long size = 0;
    for (int i = 0; i < 100; i++) {
        final String s = "" + sb + i;
        strings.add(s);
        size += s.length();
    }
    forceGC();
    long used = free - Runtime.getRuntime().freeMemory();
    if (print)
        System.out.println("Bytes per character is " + (double) used / size);
}

private static void forceGC() {
    try {
        System.gc();
        Thread.sleep(250);
        System.gc();
        Thread.sleep(250);
    } catch (InterruptedException e) {
        throw new AssertionError(e);
    }
}

デフォルトでこれを印刷します

Bytes per character is 2.0013668655941212
Bytes per character is 2.0013668655941212
Bytes per character is 2.0013606946433575
Bytes per character is 2.0013668655941212

オプションで-XX:+UseCompressedStrings

Bytes per character is 1.0014671435440285
Bytes per character is 1.0014671435440285
Bytes per character is 1.0014609725932648
Bytes per character is 1.0014671435440285
于 2011-04-12T13:56:12.840 に答える
5

実際には、UTF-8 の部分が間違っています。UTF-8 は可変長のマルチバイト エンコーディングであるため、長さが 1 ~ 4 バイトの有効な文字があります (つまり、一部の UTF-8 文字は 8 ビットであり、一部は 8 ビットです)。 16 ビット、一部は 24 ビット、一部は 32 ビット)。1 バイト文字は 8 ビットを使用しますが、さらに多くのマルチバイト文字があります。1 バイト文字しかない場合、合計で 256 の異なる文字 (別名「拡張 ASCII」) しか使用できません。英語での使用の 90% にはこれで十分かもしれません (私の素朴な推測です) が、そのサブセット以外のことを考えさえすれば、すぐにお尻を噛まれるでしょう(ナイーブ - 英語という言葉を参照してください。アスキーで)。

したがって、UTF-16 (Java が使用する) は無駄に見えますが、実際にはそうではありません。とにかく、非常に限られた組み込みシステムを使用していない限り (その場合、Java で何をしているのでしょうか?)、文字列を削減しようとするのは無意味なマイクロ最適化です。

文字エンコーディングの少し長い紹介については、たとえば次を参照してください: http://www.joelonsoftware.com/articles/Unicode.html

于 2011-04-12T12:12:29.680 に答える
2

Java はすべての「文字」を値の 2 バイト表現として内部的に保存します。ただし、UTF-8 と同じようには保存されません。たとえば、サポートされる最大値は "\uFFFF" (16 進数 FFFF、10 進数 65536)、または 11111111 11111111 バイナリ (2 バイト) ですが、これはディスク上では 3 バイトの Unicode 文字になります。

唯一の可能性のある浪費は、メモリ内の真の「シングル」バイト文字です (ほとんどの ASCII「言語」文字は、実際には 7 ビットに収まります)。文字がディスクに書き込まれるときは、指定されたエンコーディングになります (したがって、UTF-8 のシングルバイト文字は 1 バイトしか占有しません)。

違いが生じる唯一の場所は、JVM ヒープです。ただし、Java ヒープ使用量の実際の違いに気付くには、何千もの 8 ビット文字を使用する必要があります。これは、実行したすべての余分な (ハッキーな) 処理よりもはるかに重要です。

とにかく、RAM内の100万奇数の8ビット文字は、約1 MiBだけを「浪費」しています...

于 2011-04-12T13:49:10.697 に答える
2

UTF-8 を使用して byte[] を String に変換すると、各バイト (8 ビット) は UTF-8 でエンコードされた 8 ビット文字になります。

いいえ。UTF-8byte[]String使用するように変換する場合、1 ~ 6 バイトの各UTF-8 シーケンスは、 1 ~ 2 個の 16 ビット文字のUTF-16シーケンスに変換されます。

ほとんどの場合、世界中で、この UTF-16 シーケンスには単一の文字が含まれています。

西ヨーロッパと北アメリカでは、ほとんどのテキストで、この 16 ビット文字の 8 ビットのみが使用されます。ただし、ユーロ記号がある場合は、8 ビット以上が必要になります。

詳細については、Unicodeを参照してください。またはJoel Spolsky の記事.

于 2011-04-12T12:29:04.863 に答える
1

Redis (実際のサーバー) は、「バイナリ セーフ」の文字列のみを処理します。

これは、キー/値に任意のオクテット シーケンスを使用できることを意味します。文字エンコーディングを考えずに任意の C シーケンスを使用できる場合char、Java で同等のものはbyte型です。

Java の文字列は暗黙的にUTF-16. つまり、そこに任意の数字を入れることができますが、このクラスの目的は Unicode 文字データを表すことです。変換を行うメソッドは、既知のbyteエンコーディングcharから UTF-16 へのトランスコーディング操作を実行します。

Jedis がキー/値を UTF-8 として扱う場合、Redis がサポートするすべての値をサポートするわけではありません。すべてのバイト シーケンスが有効な UTF-8 であるとは限らないため、エンコードをバイナリ セーフ文字列に使用することはできません。


UTF-8 と UTF-16 のどちらがより多くのメモリを消費するかは、データによって異なります。たとえば、ユーロ記号 (€) は、UTF-8 では 3 バイト、UTF-16 では 2 バイトしか消費しません。

于 2011-04-12T15:00:11.940 に答える
0

記録のために、私は byte[] <-> String インターコンバーターの独自の小さな実装を書きました。これは、1 文字で 2 バイトごとにキャストすることによって機能します。Java の標準的な方法である new String(somebyte) と someString.getBytes() に比べて、およそ 30 ~ 40% 高速であり、半分以下のメモリしか消費しません。

ただし、既存の文字列でエンコードされたバイトまたはバイトでエンコードされた文字列とは互換性がありません。さらに、共有データで異なる JVM からメソッドを呼び出すのは安全ではありません。

https://github.com/ib84/castriba

于 2011-04-26T14:58:50.793 に答える
-1

多分それはあなたが望むものです:

// Store them into the 16 bit datatype.
char c1_8bit = 'a';
char c2_8bit = 'h';
char two_chars = (c1_8bit << 8) + c2_8bit;

// extract them
char c1_8bit = two_chars >> 8;
char c2_8bit = two_chars & 0xFF;

もちろん、このトリックは ASCII 文字 (範囲 [0-255] の文字) でのみ機能します。なんで?この方法で文字を保存したいため:
xxxx xxxx yyyy yyyywith xis char 1 and yis char 2. したがって、これは 1 文字あたり 8 ビットしかないことを意味します。また、8 ビットで作成できる最大の整数はいくつですか? 答え: 255

255= 0000 0000 1111 1111(8 ビット)。また、255 より大きい文字を使用している場合は、次のようになります:
256= 0000 0001 0000 0000(8 ビット以上)。これは、1 文字に提供する 8 ビットには収まりません。

プラス: Java は賢い人々によって開発された言語であることを覚えておいてください。彼らは自分がどこで何をしているのかを知っていました。Java API をスラストする

于 2011-04-12T12:12:51.413 に答える