使用Java将字符数组指向内存位置
对Java应用程序的一些分析表明,它需要花费大量时间将UTF-8字节数组解码为字符串对象。UTF-8字节流来自LMDB数据库,数据库中的值是Protobuf消息,这就是为什么它要对UTF-8进行如此多的解码。由此引起的另一个问题是字符串占用了大量内存,因为从内存映射解码到JVM中的字符串对象中 我想重构这个应用程序,使它不会在每次从数据库读取消息时都分配一个新字符串。我希望String对象中的底层char数组只指向内存位置使用Java将字符数组指向内存位置,java,unsafe-pointers,Java,Unsafe Pointers,对Java应用程序的一些分析表明,它需要花费大量时间将UTF-8字节数组解码为字符串对象。UTF-8字节流来自LMDB数据库,数据库中的值是Protobuf消息,这就是为什么它要对UTF-8进行如此多的解码。由此引起的另一个问题是字符串占用了大量内存,因为从内存映射解码到JVM中的字符串对象中 我想重构这个应用程序,使它不会在每次从数据库读取消息时都分配一个新字符串。我希望String对象中的底层char数组只指向内存位置 package testreflect; import java.la
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
)对象的形式存在,那么您除了进行完全转换之外别无选择。尝试其他任何事情,你都会陷入困境;e、 g.乱码文本和潜在的JVM崩溃char[]
- 如果您想要“类似字符串”的东西,可以设想实现一个自定义的
子类,它将字节封装在消息中并动态解码UTF-8。但是,有效地这样做会带来问题,尤其是将CharSequence
方法作为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
)对象的形式存在,那么您除了进行完全转换之外别无选择。尝试其他任何事情,你都会陷入困境;e、 g.乱码文本和潜在的JVM崩溃char[]
- 如果您想要“类似字符串”的东西,可以设想实现一个自定义的
子类,它将字节封装在消息中并动态解码UTF-8。但是,有效地这样做会带来问题,尤其是将CharSequence
方法作为charAt
方法实现O(1)
- 如果您只是想保存和/或比较(整个)文本,可以将它们表示为或表示为
对象。这些操作可以直接在UTF-8编码数据上执行字节[]
- 如果输入文本实际上可以以固定的8位字符大小(例如ASCII、Latin-1等)或UTF-16的字符编码发送,则可以简化事情