Возврат null из собственных методов с использованием JNI

У меня есть собственный код, который возвращает jbyteArray (то есть byte[] на стороне Java), и я хочу вернуть null. Однако у меня возникают проблемы, если я просто возвращаю 0 вместо jbyteArray.

Еще немного информации: основная логика находится в Java, для кодирования некоторых данных в поток байтов используется нативный метод. не спрашивайте.. это должно быть сделано так. Недавно нативный код пришлось немного изменить, и теперь он работает ужасно медленно. После некоторых экспериментов, включавших закомментирование всего кода в нативном методе перед возвратом, оказалось, что возврат 0 вызывает замедление. При возврате фактического jbyteArray все в порядке.

Подписи методов для моего кода:

На стороне С++:

extern "C" JNIEXPORT jbyteArray JNICALL Java_com_xxx_recode (JNIEnv* env, jclass java_this, jbyteArray origBytes, jobject message)

На стороне Java:

private static native byte[] recode(byte[] origBytes, Message message);

Нативный код выглядит примерно так:

jbyteArray javaArray;
if (error != ERROR) {
    // convert to jbyteArray
    javaArray = env->NewByteArray((jsize) message.size);
    env->SetByteArrayRegion(java_array, 0, message.size, reinterpret_cast<jbyte*>(message.buffer()));
    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
        error = ERROR;
    }
}
if (error == ERROR) {
    return 0; // Does NOT work - doesn't crash, just slows everything down horrible.
}
else {
    return javaArray; // Works perfectly.
}

Кто-нибудь знает какие-либо причины, по которым это могло произойти? Допустимо ли возвращать NULL из собственного метода вместо jbyteArray или есть другая процедура для возврата null обратно в Java. К сожалению, мне не повезло с Google.

Спасибо!

РЕДАКТИРОВАТЬ: добавлена ​​дополнительная информация.


person Community    schedule 22.07.2009    source источник
comment
Почему вы возвращаете 0 вместо массива? Почему нельзя просто вернуть пустой массив? Пожалуйста, дайте более подробную информацию о том, что должна делать эта нативная библиотека.   -  person amischiefr    schedule 22.07.2009
comment
Не могли бы вы опубликовать какой-нибудь код, хотя бы сигнатуру метода и тому подобное?   -  person Newton Falls    schedule 23.07.2009
comment
Ну, я специально хочу, чтобы Java получала null, а не пустой массив байтов. Я мог бы переписать код Java, чтобы он работал с этим, но я бы не стал, если это возможно. Я добавил сигнатуры методов.   -  person    schedule 23.07.2009
comment
Возврат нуля работает и сам по себе не вызывает замедления. Дополнительная информация, пожалуйста.   -  person user207421    schedule 01.11.2015
comment
Дополнительная информация, пожалуйста. шесть лет спустя?   -  person    schedule 29.09.2017


Ответы (3)


Это старый вопрос, но я тоже задавал его минуту назад...

Вы говорите в своем вопросе:

return 0; // Does NOT work - doesn't crash, just slows everything down horrible.

На самом деле я только что попробовал с jintArray, так как это то, что мой код должен выделять и возвращать, если не произойдет ошибка (определяемая некоторыми критериями, не связанными с этой темой), и в этом случае он должен вернуть нулевой результат.

Бывает, что возврат NULL (определенный как ((void*)0)) работает отлично и интерпретируется как null при возвращении на сторону Java. Ухудшения характеристик я не заметил. И если бы я ничего не пропустил, возврат 0 без приведения void * ничего бы не изменил.

Поэтому я не думаю, что это было причиной замедления, с которым вы столкнулись. NULL выглядит вполне нормально, чтобы вернуть null.

ИЗМЕНИТЬ:

  • Подтверждаю, возвращаемое значение не имеет ничего общего с выступлениями. Я только что протестировал один и тот же код, возвращающий нулевое значение с одной стороны, и его аналог, возвращающий объект (jintArray) с другой. Производительность аналогична для NULL, jintArray размера 0 и случайного jintArray из нескольких КБ, выделенных статически.

  • Я также попытался изменить значение поля вызывающего класса и вернуть значение void примерно с теми же характеристиками. Очень-очень немного медленнее, вероятно, из-за кода отражения, необходимого для захвата этого поля и установки его.

  • Все эти тесты были сделаны под Android, а не под Java standalones - может быть поэтому? (см. комментарии):

    • An API 17 x86 emulator running under HAXM
    • API 19, работающий в тех же условиях.
    • Два физических устройства API 19 — планшет Asus и Galaxy 5 — работают под управлением Dalvik.
person Shlublu    schedule 31.10.2015
comment
Это было давно, сейчас я работаю в другом месте. Тем не менее, я помню это, и в то время, если я возвращал 0/NULL, это было очень медленно, но если я возвращал что-то еще (например, пустой массив), это было не так. Я не знаю, в чем была моя проблема, но я могу гарантировать, что JVM, которую я использовал в 2009 году, отличается от той, которую вы использовали в 2015 году. Это была моя JVM? Это была моя конфигурация? Это была моя установка JNI? Мы никогда не узнаем, но я точно знаю, что возврат 0/NULL делал его очень медленным. Я не помню, как я решил это в конце. Я предполагаю, что либо вернул пустой массив, либо «нулевой» экземпляр объекта. - person ; 27.06.2016
comment
Я думал, что Dalvik немного другой? Во-первых, Dalvik — это виртуальная машина на основе регистров, а виртуальные машины Oracle (на момент написания этого вопроса — Sun) JVM основаны на стеке. Сейчас я не планирую проводить повторное тестирование на другой JVM, но я бы не ожидал таких же результатов лично от Dalvik. - person ; 27.06.2016
comment
Привет @Дэн! Да, это имеет абсолютный смысл. Когда я писал это, я использовал Dalvik, а сейчас это даже другой, называемый Art. Это могло бы объяснить, это правда. Я обновил свой раздел редактирования соответственно. - person Shlublu; 27.06.2016

В вашем коде есть некоторая асимметрия, которая бросилась мне в глаза: вы никогда не выбираете тип возвращаемого объекта, за исключением случаев, когда вы возвращаете «ничего». Очевидно, объект env решает, как разместить javaSrray, так почему бы не попросить его вернуть какой-нибудь пустой массив? Возможно, возвращаемый вами 0 нужно обрабатывать особым образом при маршалинге между jni и java.

person xtofl    schedule 23.07.2009
comment
Да, я предположил, что вместо возврата 0 (который, кажется, РАБОТАЕТ, т.е. я получаю ноль обратно на стороне Java, но по какой-то странной странной причине он действительно медленный.. может быть, JNI рассматривает это как случай ошибки и возвращает ноль из-за это как побочный эффект?, и дополнительная обработка ошибок замедляет его??), что я должен возвращать какой-то объект jnull, но я не искал его, на самом деле... - person ; 23.07.2009

Вы пытались вернуть ссылку NULL?

Это не проверено (в данный момент у вас нет под рукой среды разработки JNI), но вы должны иметь возможность создать новую глобальную ссылку на NULL и вернуть ее следующим образом:

return (*env)->NewGlobalRef(env, NULL);

РЕДАКТИРОВАТЬ При этом вы проверяете, возникает ли исключение, но не очищаете его. Это, насколько я понимаю, означает, что он все еще «выброшен» на уровень Java, поэтому вы должны иметь возможность использовать его как индикатор ошибки; тогда не имеет значения, что возвращает функция. На самом деле, вызов функции JNI, отличной от ExceptionClear()/ExceptionDescribe(), при возникновении исключения не является «безопасным» в соответствии с документацией. То, что функции "медленные", может быть вызвано тем, что функция ExceptionDescribe() записывает отладочную информацию.

Итак, если я правильно понимаю, это должна быть функция с хорошим поведением, выдающая исключение при первом возникновении ошибки и возвращающая NULL при каждом последующем вызове (пока «ошибка» не будет очищена):

if (error != ERROR) {
    jbyteArray javaArray = env->NewByteArray((jsize) message.size);
    env->SetByteArrayRegion(javaArray, 0, message.size, reinterpret_cast<jbyte*>(message.buffer()));
    if (env->ExceptionOccurred()) {
        error = ERROR;
        return 0;
    }
    return javaArray;
} else {
    return env->NewGlobalRef(NULL);
}

Опять же, это не проверено, поскольку сейчас у меня нет доступной среды JNI.

person Christoffer    schedule 23.07.2009
comment
Спасибо за это. Что ж, код по-прежнему работает медленно, если все закомментировано, КРОМЕ возврата 0, поэтому это не может быть вызов ExceptionDescribe(). Тем не менее, это хороший момент, и ваш код немного чище, чем мой. - person ; 23.07.2009
comment
Какую виртуальную машину вы используете? Если это с открытым исходным кодом, посмотрите, можете ли вы узнать, есть ли какой-то дополнительный шаг проверки или что-то еще, если нативная функция возвращает null :) Возможно, лучше создать исключение в нативном коде и для случая «иначе»? - person Christoffer; 23.07.2009
comment
Нет, используя виртуальную машину Sun. Да, я предполагаю, что дополнительный код обработки ошибок запускается, когда возвращаемое значение равно нулю. В любом случае, было бы неплохо узнать, в чем здесь дело, но я все равно могу исправить это другими способами. - person ; 23.07.2009
comment
NewGlobalRef возвращает jobject, а не jbytearray, и кажется, что конвертация недоступна, можете ли вы проверить свое решение? [Кроме того, мне непонятно, какова семантика глобальной ссылки на NULL... NULL не является java-объектом, он не может быть собран мусором... Надеюсь :) Так что нам не нужно чтобы иметь ссылку на него, чтобы передать его.] - person Max Galkin; 12.01.2015
comment
@MaxGalkin jbyteArray на самом деле является typedef jarray, который сам является typedef jobject, который является typedef void *. - person Shlublu; 01.11.2015