首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免在Java字符(16位)中存储UTF-8字符(8位)时的内存浪费。二合一?

如何避免在Java字符(16位)中存储UTF-8字符(8位)时的内存浪费。二合一?
EN

Stack Overflow用户
提问于 2011-04-12 20:02:29
回答 7查看 3.9K关注 0票数 6

恐怕我有一个关于一个相当饱和的主题的细节的问题,我搜索了很多次,但是没有找到一个明确的答案来回答这个明显的-不重要的问题:

当使用UTF-8将byte[]转换为字符串时,每个字节(8位)都会变成一个由UTF-8编码的8位字符,但在java中,每个UTF-8字符都保存为16位字符。对吗?如果是,这意味着每个愚蠢的java字符只使用前8位,并消耗两倍的内存?这也是正确的吗?我想知道这种浪费的行为怎么能被接受。

有一个8位的伪串是不是有一些诀窍?这实际上会减少内存消耗吗?或者,有没有一种方法可以在一个>two< 16位字符中存储Java8位字符来避免这种内存浪费?

感谢你给我解开的答案。

编辑:嗨,感谢大家的回复。我知道UTF-8的可变长度属性。然而,由于我的源是8位字节,我理解(显然是错误的)它只需要8位UTF-8字。UTF-8转换是否真的保存了您在CLI上执行"cat somebinary“时看到的奇怪符号?我认为UTF-8只是以某种方式将字节的每个可能的8位字映射到UTF-8的一个特定的8位字。不对?我考虑过使用Base64,但它很糟糕,因为它只使用了7位。

重新制定的问题:有没有更聪明的方法将字节转换成字符串?我最喜欢的是将byte[]转换为char[],但我仍然有16位字。

其他用例信息:

我采用Jedis ( NoSQL Redis的java客户端)作为hypergraphDB的“原始存储层”。因此,jedis是另一个“数据库”的数据库。我的问题是,我必须一直向jedis提供byte[]数据,但在内部,>Redis< (实际的服务器)只处理“二进制安全”字符串。因为Redis是用C语言写的,所以一个字符是8位的,而AFAIK不是7位的ASCIII。然而,在Jedis中,java世界中,每个字符在内部都是16位长。我还不理解这段代码,但是我想jedis会把这个java的16位字符串转换成一个符合Redis的8位字符串((here)。它说它扩展了FilterOutputStream。我希望完全绕过byte[] <->字符串转换,使用Filteroutputstream...?)

现在我在想:如果我必须一直转换byte[]和字符串,数据范围从非常小到可能非常大,在java中将每个8位字符作为16位传递,不是非常浪费内存吗?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2011-04-12 21:56:13

有一个8位的伪串是不是有一些诀窍?

是的,请确保您拥有最新版本的Java。;)

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

-XX:+UseCompressedStrings对字符串使用byte[],它可以表示为纯ASCII码。(在Java 6 Update 21性能版本中引入)

编辑:此选项在Java 6更新22中不起作用,并且在Java 6更新24中默认为打开。注意:此选项可能会使性能降低约10%。

下面的程序

代码语言:javascript
复制
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);
    }
}

缺省情况下打印此内容

代码语言:javascript
复制
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

代码语言:javascript
复制
Bytes per character is 1.0014671435440285
Bytes per character is 1.0014671435440285
Bytes per character is 1.0014609725932648
Bytes per character is 1.0014671435440285
票数 9
EN

Stack Overflow用户

发布于 2011-04-12 20:12:30

实际上,您对UTF-8部分的理解是错误的: UTF-8是一种可变长度的多字节编码,因此存在长度为1-4字节的有效字符(换句话说,一些UTF-8字符是8位的,一些是16位的,一些是24位的,还有一些是32位的)。虽然1字节字符占据了8位,但还有更多的多字节字符。如果您只有1个字节的字符,则总共只能有256个不同的字符(也称为“扩展的ASCII");这可能足以满足英语中90%的使用(我的天真猜测),但只要你想到这个子集之外的任何东西,就会咬你的屁股(参见单词天真-英语,但不能只用ASCII编写)。

因此,尽管UTF-16 ( Java使用的)看起来很浪费,但实际上并非如此。无论如何,除非您在一个非常有限的嵌入式系统上(在这种情况下,您在那里用Java做什么?),否则试图精简字符串是毫无意义的微优化。

有关字符编码的稍微长一点的介绍,请参见例如:http://www.joelonsoftware.com/articles/Unicode.html

票数 5
EN

Stack Overflow用户

发布于 2011-04-12 20:29:05

使用UTF-8将byte[]转换为字符串时,每个字节(8位)都会变成一个由UTF-8编码的8位字符

不是的。当使用UTF-8将byte[]转换为String时,每个1-6字节的UTF-8 序列被转换为1-2个16位字符的UTF-16 序列

在世界范围内,几乎所有情况下,此UTF-16序列都包含单个字符。

在西欧和北美,对于大多数文本,仅使用此16位字符的8位。然而,如果你有一个欧元符号,你将需要超过8位。

有关详细信息,请参阅Unicode。或者Joel Spolsky's article

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5634855

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档