#java #c #linux #memory-leaks #java-native-interface
#java #c #linux #утечки памяти #java-native-interface
Вопрос:
У меня есть Java-приложение, которое использует библиотеку C для получения кадров с камеры. Что я делаю, так это сохраняю данные фрейма в библиотеке c и передаю метаданные в Java. Затем из Java он выполняет вызов через JNI для получения последних данных кадра этой камеры.
Проблема, с которой я сталкиваюсь, заключается в том, что в одной из этих функций у меня, похоже, утечка памяти. После запуска обработки она увеличивается до нескольких ГБ за считанные секунды. Однако, если я запускаю библиотеку C для независимого запуска (в основном точно такой же код, только без компонента JNI), утечки памяти нет.
Вот две функции C:
JNIEXPORT jbyteArray JNICALL
Java_com_c_C_1Wrapper_nGet_1Image (JNIEnv *env, jobject jobj, jint i_) {
_jobj = jobj;
jint rs = (*env)->GetJavaVM(env, amp;jvm);
assert (rs == JNI_OK);
jbyteArray img_data;
int cam_id = i_;
if (!frame_data[cam_id].lock) {
frame_data[cam_id].lock = TRUE;
img_data = (*env)->NewByteArray(env, frame_data[cam_id].data_size);
(*env)->SetByteArrayRegion(env, img_data, 0,
frame_data[cam_id].data_size,
(jbyte*)frame_data[cam_id].img);
frame_data[cam_id].lock = FALSE;
}
return img_data;
}
void
send_frame_data(guint camera_id, gint data_size, void *new_frame,
gint width, gint height, const char *meta_data) {
JNIEnv *env;
jint rs = (*jvm)->AttachCurrentThread(jvm, amp;env, NULL);
assert (rs == JNI_OK);
if (!frame_data[camera_id].lock) {
frame_data[camera_id].lock = TRUE;
free(frame_data[camera_id].img);
frame_data[camera_id].img = malloc(data_size);
memcpy(frame_data[camera_id].img, new_frame, data_size);
frame_data[camera_id].width = width;
frame_data[camera_id].height = height;
frame_data[camera_id].data_size = data_size;
frame_data[camera_id].lock = FALSE;
}
jbyteArray jMetaData = (*env)->NewByteArray(env, strlen(meta_data));
(*env)->SetByteArrayRegion(env, jMetaData, 0, strlen(meta_data), meta_data);
(*env)->CallStaticVoidMethod(env, jSentryCore_class, jImagePreview, jMetaData);
(*env)->DeleteLocalRef(env, jMetaData);
}
Следует отметить одну вещь: объект *new_frame освобождается от вызывающего метода.
Сторона Java проста:
@Override
public void ImagePreview(String metaData) {
// parse metaData
byte[] image = C_Wrapper.Get_Image(cameraId);
// do something with data
}
Я несколько раз переставлял логику, но безуспешно. Что бы я ни делал, всякий раз, когда задействована часть JNI, происходит серьезная утечка памяти.
Комментарии:
1. Вы пробовали использовать инструмент для поиска утечек, такой как AddressSanitizer?
2. У меня нет, но я должен упомянуть, что утечка памяти не является большой проблемой в моей среде разработки с 32 ГБ памяти. Проблема действительно очевидна (и является проблемой) на меньшем ARM-устройстве с гораздо меньшим объемом памяти, и похоже, что AddressSanitizer не поддерживает ARM.
3. Хорошая особенность таких инструментов в том, что они обнаруживают утечки, когда они происходят, а не только тогда, когда утечки становятся заметными для пользователя. Поэтому используйте инструмент на любой системе, которая у вас есть, которая его поддерживает.
Ответ №1:
Это не утечка памяти, вы просто выделяете память быстрее, чем сборщик мусора Java может ее освободить. Предполагая 1080p @ 60 с 16 бит / с, это 250 МБ / с. C может справиться с этим, потому что, скорее malloc
всего, вернет вам буфер, который вы только free
что создали, если размеры равны.
Вы должны прекратить вызывать NewByteArray
для каждого вызова Get_Image
. Вместо этого сохраните пул byte[]
объектов фиксированного размера и измените Get_Image
его, чтобы использовать буфер из пула. Вам также нужно будет вернуть буфер в пул, когда ваш Java-код будет выполнен с ним. В зависимости от того, что ваш Java-код делает с изображением, вы также можете исследовать, используя ByteBuffer
вместо этого direct s: вы можете напрямую записывать в них из C вместо вызова memcpy
.
Это ограничит ваш объем памяти до вашего количества буферов. Имейте в виду возможность того, что при вызове Get_Image буферы в настоящее время недоступны, и определите стратегию борьбы с этим.
Комментарии:
1. Я действительно нашел, откуда шло увеличение памяти. Вы частично правы в том, что что-то увеличивается быстрее, чем оно может быть обработано, но этого не было в этом разделе кода. Я действительно в какой-то степени реализовал ваше предложение, но потом подумал, что имею дело с утечкой памяти, и отказался от этой идеи, пока не выяснил проблему. Спасибо за предложение.