Некоторый анализ Java-приложения показал, что он тратит много времени на декодирование байтовых массивов UTF-8 в объекты String.Поток байтов UTF-8 поступает из базы данных LMDB, а значения в базе данных являются сообщениями Protobuf, поэтому он так сильно декодирует UTF-8.Это вызвано еще одной проблемой: строки занимают большой кусок памяти из-за декодирования из карты памяти в объект String в JVM.
Я хочу реорганизовать это приложение, чтобы оно не выделялосьновая строка каждый раз, когда она читает сообщение из базы данных.Я хочу, чтобы базовый массив char в объекте String просто указывал на область памяти.
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 с использованием пакета Unsafe.Это должно уменьшить количество процессорного времени, которое приложение тратит на декодирование байтов UTF-8.
Однако это не решает проблему с памятью.Есть ли способ, чтобы массив символов указывал на область памяти и вообще избегал выделения памяти?Отказ от копирования в целом сократит количество ненужных выделений, которые JVM делает для этих строк, оставляя ОС больше места для кэширования записей из базы данных LMDB.