Может ли JNI вызывать метод для объекта во время инициализации?

#java #initialization #java-native-interface #final

#java #инициализация #java-native-interface #Финал

Вопрос:

Класс Java выполняет что-то вроде следующего

 public class Foo {

    private final NativeCallbackHandler handler;

    public Foo(NativeCallbackHandler handler) {
        // I've shortened this for exposition, callSomeNativeMethod 
        // really happens in a superclass that I don't own (and is 
        // part of the lib that gives me the native part)
        callSomeNativeMethod();
        this.handler = handler;
    }

    public void handleNativeCallback(Object args) {
        this.handler.callback(args);
    }

    private native int callSomeNativeMethod();
}
  

Можно предположить, что собственный метод делает что-то, что может привести к вызову собственного кода handleNativeMethod

У меня есть 2 связанных вопроса

  1. Я считаю, что машинный код должен вызывать дескриптор для этого объекта, а также вызывать GetMethodID , чтобы получить доступ к вызываемому методу, возможно ли, чтобы этот машинный код вызывал метод до полной инициализации объекта?
  2. Если может, какова семантика неинициализированного конечного поля?

если 1 — «да», то я ожидаю, что 2 взорвется при доступе к нему, и, следовательно, я полагаю, нам нужно было бы сделать это AtomicReference , чтобы получить к нему безопасный доступ без сбоев.

Обратите внимание, что я не могу контролировать поведение собственной библиотеки.

Комментарии:

1. Есть ли какая-то причина, ПО которой вы вызываете ‘callNativeMethod’ перед установкой этого поля обработчика?

2. разве вы не можете просто создать это, запустить и выяснить? кажется, что вы уже на 90% готовы…

3. потому что я не владею кодом в собственной библиотеке, и это то, как он может вести себя. Проблема в том, что довольно маловероятно, что он вызовет этот метод (это проблема с синхронизацией), и я не могу заставить его это сделать. Это зависит от всевозможных событий, происходящих в сети, а также от того, когда поступают определенные сообщения и в каком порядке, что означает, что их очень сложно детерминированно воссоздать. Следовательно, мне нужно знать, есть ли у меня теоретически проблема, и если да, то в чем проблема

4. У вас есть Foo? Разве вы не можете просто поменять местами строки ‘callNativeMethod()’ и ‘thisHandler = headler’?

5. @Andrew, отредактировал пример кода, чтобы попытаться прояснить, почему я не могу этого сделать

Ответ №1:

Похоже, что это возможно. Машинный код не применяет final ограничение.

Из http://java.sun.com/docs/books/jni/html/pitfalls.html#36197:

10.9 Нарушение правил контроля доступа

JNI не применяет ограничения контроля доступа к классам, полям и методам, которые могут быть выражены на уровне языка программирования Java с помощью модификаторов, таких как private и final. Можно написать машинный код для доступа к полям объекта или их изменения, даже если выполнение этого на уровне языка программирования Java привело бы к исключению IllegalAccessException. Вседозволенность JNI была сознательным дизайнерским решением, учитывая, что машинный код в любом случае может получить доступ к любой ячейке памяти в куче и изменить ее.

Машинный код, который обходит проверки доступа на уровне исходного языка, может иметь нежелательные последствия для выполнения программы. Например, может возникнуть несоответствие, если собственный метод изменяет конечное поле после того, как JIT-компилятор осуществил встроенные обращения к полю. Аналогично, собственные методы не должны изменять неизменяемые объекты, такие как поля в экземплярах java.lang.String или java.lang.Целое число. Это может привести к нарушению инвариантов в реализации платформы Java.

Это не определяет поведение при обращении к неинициализированной конечной ссылке, но мы, вероятно, можем сделать довольно хорошее предположение.

Лично я бы попытался избежать проблемы, либо:

  • Убедиться, что все было инициализировано перед обратным вызовом
  • Ничего не делать во время обратного вызова, пока не будет установлен флаг, свидетельствующий о завершении инициализации.

Ответ №2:

Вызов handleNativeCallback из конструктора суперкласса приведет к NullPointerException , потому что он вызывается до установки обработчика. Нет разницы, был ли вызов выполнен с помощью JNI или чистого кода Java.

Комментарии:

1. 1 Хороший момент — и это происходит независимо от того, является поле окончательным или нет. Вот пример чистой Java: ideone.com/0HoFk .