使用Java将字符数组指向内存位置

使用Java将字符数组指向内存位置,java,unsafe-pointers,Java,Unsafe Pointers,对Java应用程序的一些分析表明,它需要花费大量时间将UTF-8字节数组解码为字符串对象。UTF-8字节流来自LMDB数据库,数据库中的值是Protobuf消息,这就是为什么它要对UTF-8进行如此多的解码。由此引起的另一个问题是字符串占用了大量内存,因为从内存映射解码到JVM中的字符串对象中 我想重构这个应用程序,使它不会在每次从数据库读取消息时都分配一个新字符串。我希望String对象中的底层char数组只指向内存位置 package testreflect; import java.la

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

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

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数组,并使用不安全的包在String对象中设置底层数组。这将减少应用程序在解码UTF-8字节时浪费的CPU时间


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

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

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

嗯。。。没有

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

真正的UTF-8到
String
转换需要将表示UTF-8码点的1到4字节(代码单元)转换为表示UTF-16中相同码点的1或2个16位代码单元。这不能使用普通内存副本来完成

如果您不熟悉,那么值得阅读,以便了解文本的编码方式


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

  • 如果数据必须以
    String
    (或
    StringBuilder
    char[]
    )对象的形式存在,那么您除了进行完全转换之外别无选择。尝试其他任何事情,你都会陷入困境;e、 g.乱码文本和潜在的JVM崩溃

  • 如果您想要“类似字符串”的东西,可以设想实现一个自定义的
    CharSequence
    子类,它将字节封装在消息中并动态解码UTF-8。但是,有效地这样做会带来问题,尤其是将
    charAt
    方法作为
    O(1)
    方法实现

  • 如果您只是想保存和/或比较(整个)文本,可以将它们表示为或表示为
    字节[]
    对象。这些操作可以直接在UTF-8编码数据上执行

  • 如果输入文本实际上可以以固定的8位字符大小(例如ASCII、Latin-1等)或UTF-16的字符编码发送,则可以简化事情


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

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

      嗯。。。没有

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

      真正的UTF-8到
      String
      转换需要将表示UTF-8码点的1到4字节(代码单元)转换为表示UTF-16中相同码点的1或2个16位代码单元。这不能使用普通内存副本来完成

      如果您不熟悉,那么值得阅读,以便了解文本的编码方式


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

      • 如果数据必须以
        String
        (或
        StringBuilder
        char[]
        )对象的形式存在,那么您除了进行完全转换之外别无选择。尝试其他任何事情,你都会陷入困境;e、 g.乱码文本和潜在的JVM崩溃

      • 如果您想要“类似字符串”的东西,可以设想实现一个自定义的
        CharSequence
        子类,它将字节封装在消息中并动态解码UTF-8。但是,有效地这样做会带来问题,尤其是将
        charAt
        方法作为
        O(1)
        方法实现

      • 如果您只是想保存和/或比较(整个)文本,可以将它们表示为或表示为
        字节[]
        对象。这些操作可以直接在UTF-8编码数据上执行

      • 如果输入文本实际上可以以固定的8位字符大小(例如ASCII、Latin-1等)或UTF-16的字符编码发送,则可以简化事情


      如果性能非常关键,为什么不实施CharSequence以满足您的需要?字符串只是一种特殊类型的字符序列,强调的是不变性和封装,而不是性能。您的CharSequence实现可以考虑其他优先级,例如接受字节[]并执行