首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用Java unsafe将char数组指向内存位置

使用Java unsafe将char数组指向内存位置
EN

Stack Overflow用户
提问于 2018-10-13 11:39:34
回答 1查看 828关注 0票数 0

对Java应用程序的一些分析表明,它花费了大量时间将UTF-8字节数组解码为String对象。UTF-8字节流来自LMDB数据库,数据库中的值是Protobuf消息,这就是它对UTF-8解码如此之多的原因。由此引起的另一个问题是,由于在JVM中将内存映射解码为字符串对象,因此字符串占用了大量内存。

我想重构这个应用程序,这样它就不会在每次从数据库中读取消息时都分配一个新的字符串。我希望String对象中的底层char数组只指向内存位置。

代码语言:javascript
复制
package testreflect;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class App {
    public static void main(String[] args) throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe UNSAFE = (Unsafe) field.get(null);

        char[] sourceChars = new char[] { 'b', 'a', 'r', 0x2018 };

        // Encoding to a byte array; asBytes would be an LMDB entry
        byte[] asBytes = new byte[sourceChars.length * 2];
        UNSAFE.copyMemory(sourceChars, 
                UNSAFE.arrayBaseOffset(sourceChars.getClass()), 
                asBytes, 
                UNSAFE.arrayBaseOffset(asBytes.getClass()), 
                sourceChars.length*(long)UNSAFE.arrayIndexScale(sourceChars.getClass()));

        // Copying the byte array to the char array works, but is there a way to
        // have the char array simply point to the byte array without copying?
        char[] test = new char[sourceChars.length];
        UNSAFE.copyMemory(asBytes, 
                UNSAFE.arrayBaseOffset(asBytes.getClass()), 
                test, 
                UNSAFE.arrayBaseOffset(test.getClass()), 
                asBytes.length*(long)UNSAFE.arrayIndexScale(asBytes.getClass()));

        // Allocate a String object, but set its underlying 
        // byte array manually to avoid the extra memory copy   
        long stringOffset = UNSAFE.objectFieldOffset(String.class.getDeclaredField("value"));
        String stringTest = (String) UNSAFE.allocateInstance(String.class);
        UNSAFE.putObject(stringTest, stringOffset, test);
        System.out.println(stringTest);
    }
}

到目前为止,我已经了解了如何将字节数组复制到char数组,并使用Unsafe包在String对象中设置底层数组。这应该会减少应用程序在解码UTF-8字节上浪费的CPU时间。

但是,这并不能解决内存问题。有没有办法让char数组指向一个内存位置,并完全避免内存分配?完全避免复制将减少JVM为这些字符串进行的不必要分配的数量,从而为操作系统缓存来自LMDB数据库的条目留下更多空间。

EN

回答 1

Stack Overflow用户

发布于 2018-10-13 13:35:22

我认为你在这里采取了错误的方法。

到目前为止,我已经了解了如何将字节数组复制到字符数组,并使用

包在String对象中设置底层数组。这应该会减少应用程序在解码UTF-8字节上浪费的CPU时间。

呃..。不是的。

使用内存复制从byte[]复制到char[]是行不通的。目标char[]中的每个char实际上将包含原始char[]中的2个字节。如果你随后尝试将char[]包装成String,你会得到一种奇怪的mojibake。

真正的UTF-8到String的转换所做的是将表示UTF-8码点的1到4个字节(码元)转换成表示UTF-16中相同码点的1或2个16位码元。这不能使用普通的内存拷贝来完成。

如果您不熟悉它,那么有必要阅读Wikipedia article on UTF-8,这样您就可以理解文本是如何编码的。

解决方案取决于您打算如何处理文本数据。

  • 如果数据必须是String (或StringBuilderchar[])对象的形式,那么除了执行完整转换之外,您别无选择。
  • 如果你想要“类似字符串”的东西,你可以实现一个自定义的CharSequence子类,它包装消息中的字节并动态解码UTF-8。
  • 如果您只是想保存和/或比较(整个)文本,可以通过将它们表示为或在byte[]对象中实现。
  • 如果您只是想保存和/或比较(整个)文本,则可以通过将它们表示为或在byte[]对象中实现。这些操作可以直接在UTF-8编码的数据上执行。
  • 如果输入文本实际上可以使用固定的8位字符大小(例如,ASCII、拉丁文-1等)以字符编码发送,或者作为UTF-16发送,这将简化事情。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52789313

复制
相关文章

相似问题

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