jak uniknąć utraty pamięci podczas przechowywania znaków UTF-8 (8 bitów) w symbolu Java (16 bitów). dwa w jednym?

obawiam się, mam pytanie po Części jest dość przeładowany tematy, dużo szukałem wokół, ale nie mogłem znaleźć jednoznacznej odpowiedzi na tę konkretną oczywiste-imho-ważne, problem:

podczas konwersji byte[] wiersz za pomocą UTF-8 każdy bajt (8 bitów) staje się 8-bitowym symbolem, закодированным UTF-8, ale każdy symbol UTF-8 jest zapisywane jako 16-bitowy znak w języku java. Czy to prawda? Jeśli tak, to oznacza, że każdy głupi symbol Java używa tylko pierwsze 8 bitów, i zużywa podwoić pamięć? To też prawda? Ciekawe, ile dopuszczalne jest niepotrzebne zachowanie?.

czy nie ma jakiegoś triku, aby mieć pseudo-wiersz, który 8 bitów? Czy to spowoduje mniejsze zużycie pamięci? Lub, być może istnieje sposób, aby zachować >dwie litery

dzięki za wszelkie odpowiedzi deconfusing...

edytuj: witam, dziękuję wszystkim za odpowiedzi. Wiedziałem o właściwości zmiennej długości UTF-8. Jednak, ponieważ moje źródło-bajt, który jest 8-bitowym, zrozumiałem (prawdopodobnie błędnie), że potrzebuje tylko 8-bitowe słowa UTF-8. Konwersja UTF-8 jest naprawdę zachowuje się dziwne znaki, które widzisz, kiedy na CLI robisz "cat somebinary" ? Myślałem, że UTF-8 po prostu w jakiś sposób jest używany do mapowania każdego z możliwych 8-bitowych słów bajty z jednym konkretnym 8-bitowym słowem UTF-8. Nieprawidłowo? Myślałem o użyciu Base64, ale to źle, bo wykorzystuje tylko 7 bitów..

pytania reframed: czy bardziej inteligentny sposób przekonwertować bajtów na wiersz? Może być, ulubionym było po prostu wrzucić byte [], char [], ale to wciąż mam 16-bitowe słowa.

dodatkowe opcje korzystania z nieruchomości:

ja адаптируюсь jedi (java-klient dla NoSQL Redis) jako "prymitywnego warstwy przechowywania" dla hypergraphDB. Tak więc, jedi-jest to baza danych dla innego "bazy danych". Mój problem polega na tym, że muszę stale karmić jedi danych byte [], ale wewnętrznie, > Redis string i korzystać z tego Filteroutputstream...? )

teraz ja ciekawe: gdyby musiałem cały czas nawracać byte [], String, z datami od bardzo małych do potencjalnie bardzo dużych, czy nie ma ogromnej utraty pamięci, aby każdy 8-bitowy znak przekazany jako 16bit w java?


czy nie ma jakiegoś triku, aby mieć pseudo-wiersz, który 8 bitów?

tak, upewnij się, że masz zaktualizowaną wersję oprogramowania Java. ;)

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

- XX: + UseCompressedStrings używają byte [], aby wierszy, które mogą być przedstawione jako czyste ASCII. (Przedstawiony w kwestii wydajności Java 6 Update 21)

EDIT: to opcja nie działa w Java 6 update 22 i nie jest domyślnie włączona w Java 6 update 24. Uwaga: wygląda na to, że opcja ta może spowodować spadek wydajności o około 10%.

program

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);
    }
}

drukuje to domyślnie

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

z parametrem -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

faktycznie, masz w kodowaniu UTF-8 części prawidłowo: UTF-8 jest o zmiennej długości wielobajtowy ciąg znaków, w ten sposób, jest dozwolone znaki od 1 do 4 bajtów (innymi słowy, niektóre znaki UTF-8 8-bitowy, niektóre 16-bitowe, niektóre z nich 24-bit, a niektóre programy 32-bitowe). Chociaż 1-bajtowe znaki zajmują 8 bitów, jest jeszcze wiele znaków wielobajtowych. Jeśli masz tylko 1-bajtowe znaki, to pozwoli ci mieć tylko 256 różnych znaków (a.k.a. "Extended ASCII"); to może być na tyle 90% użycia w języku angielskim (my naiwny guesstimate), ale ugryzie cię w tyłek, jak tylko można nawet chyba czegokolwiek poza tej domenie (patrz słowo naïve-English, ale nie może być pisane tylko za pomocą ASCII).

Tak, choć UTF-16 (który wykorzystuje Java) wygląda marnotrawstwo, właściwie to nie jest tak. W każdym razie, jeśli nie jesteś w bardzo ograniczonej wbudowana w system (w tym przypadku, co robisz tam z Java?), próba przyciąć wiersza jest bezsensowne микрооптимизация.

do nieco dłuższego wprowadzenie do kodowania znaków, patrz, na przykład:http://www.joelonsoftware.com/articles/Unicode.html


podczas konwersji byte[] wiersz za pomocą UTF-8 każdy bajt (8 bitów) staje się 8-bitowym symbolem, закодированным UTF-8

nie ma. Podczas konwersji byte[]String za pomocą UTF-8, każdy UTF-8 sekwencja z 1-6 bajtów przekształca się w UTF-16 sekwencja 1-2 16-bitowych znaków.

praktycznie we wszystkich przypadkach na całym świecie ta sekwencja UTF-16 zawiera jeden znak.

W Zachodniej Europa i Ameryka Północna,większość text, używany tylko 8 bit to 16-bit znaku. Jednak, jeśli masz znak euro, trzeba będzie więcej niż 8 bitów.

więcej informacji można znaleźć W sekcji Unicode. Lub artykuł Joela Спольского.


Java przechowuje wszystko to "symbole" wewnętrznie jak dwa bajtów prezentacji wartości. Jednak nie są one przechowywane, tak samo jak UTF-8. Na przykład, maksymalna obsługiwana wartość "\uFFFF " (FFFF hex, dec 65536) lub 11111111 11111111 binarny (dwa bajty) - ale to będzie 3-bajtowy znak Unicode na dysku.

jedyna możliwa utrata - to naprawdę "tylko" symbole w pamięci (większość znaków ASCII "języka" w rzeczywistości wpisują się w 7bits). Gdy znaki są zapisywane w dysk, nadal będą w określonym formacie (dlatego jednobajtowe znaki UTF-8 będą zajmować tylko jeden bajt).

jedyne miejsce, gdzie ma to znaczenie, to stos JVM. Jednak trzeba mieć tysiące znaków 8-bitowych, aby zauważyć jakąkolwiek różnicę w użyciu sterty Java, która będzie o wiele mogą zostać zneutralizowane przez całą więcej (hakerów) opracowanie, które zrobiłeś.

milion z hakiem znaków 8-bitowych w pamięci RAM tylko "wydaje" około 1 MiB w każdym przypadku...


Рэдис (rzeczywisty serwer) ma do czynienia tylko z "opcjami bezpieczne" wierszami.

Ja rozumiem to tak, że można użyć dowolnych sekwencji oktetów do kluczy/wartości. Jeśli można użyć dowolnego C char sekwencja bez myśli do kodowania znaków, wtedy odpowiednikiem w języku Java jest byte typ.

wiersze w języku Java niejawnie UTF-16. Mam na myśli, można wstawić tam dowolne liczby, ale celem klasy - reprezentować dane w formacie Unicode. Metody, które sprawiają,bytechar konwersji wykonują operacje transkodowania ze znanej kodowania w UTF-16.

jeśli Jedis obsługuje klucze / wartości jako UTF-8, to on nie będzie wspierać każdą wartość, która obsługuje Redis. nie każda sekwencja bajtów jest ważna UTF-8, więc kodowanie nie może służyć do binarnych bezpiecznych ciągów.


zużywa czy UTF-8 lub UTF-16 więcej pamięci, zależy od danych-znak euro (€) na przykład, zużywa trzy bajty w UTF-8 i tylko dwa w UTF-16.


po prostu do nagrywania napisałem swoją własną niewielką realizację Byte[] String interconverter, który działa, rzucając co 2 bajty 1 symbol. To o około 30-40% szybciej i zużywa (może być mniej) połowa pamięci standardowej metody Java: nowa linia(somebyte) i someString.getBytes ().

jednak jest on skojarzony z istniejącymi ciągami kodowanych bajtów lub байтовыми zakodowanych ciągów. Ponadto, nie jest bezpiecznie wywołać metodę z różnych JVMs na shared dane.

https://github.com/ib84/castriba


może być, to jest to, co chcesz:

// 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;

oczywiście, ta sztuczka działa tylko ze znaków ASCII (znaki w zakresie [0-255]). Dlaczego? Bo chcesz przechowywać swoje symbole w taki sposób:
xxxx xxxx yyyy yyyyx to char 1 i y kula 2. To znaczy, masz tylko 8 bitów na symbol. I jak największą liczbę całkowitą można zrobić z 8 bitów? Odpowiedź: 255

255 = 0000 0000 1111 1111 (8 bitów). I kiedy używasz char > 255, więc u ciebie będzie to:
256 = 0000 0001 0000 0000 (ponad 8 bitów), który nie mieści się w 8-bitowy, który podajesz do 1 znaku.

Plus: należy pamiętać, że Java to język opracowany przez inteligentnych ludzi. Oni wiedzieli, co robią. wklej Java API