Подтвердить что ты не робот

JVM работает не так, как ожидалось, с кодом JNI С++, содержащим класс с именем "Node"

Сам себе и некоторым товарищам по команде не удалось понять, почему следующий фрагмент кода не даст правильного вывода при использовании версий JVM версии 1.6u23 до 1.6u31 (последние из этой публикации). Этот фрагмент кода представляет собой упрощение более крупной проблемы:

UPDATE: слегка изменил пример, чтобы сосредоточить внимание на проблеме, которая, по-видимому, не вызвана вызовом virtual_function().

UPDATE: упростить пример еще больше, основываясь на комментариях к дате.

NodeTester.cpp:

#include <iostream>
#include <jni.h>

class Node {
  public:
    Node () :m_counter(0) {}
    virtual ~Node () {}

    virtual void virtual_function () {
      m_counter += 10;
    }

    void non_virtual_function () {
      m_counter += 1;
    }

    int get_counter () {
      return m_counter;
    }

  private:
    int m_counter;

};

extern "C" {
  JNIEXPORT void JNICALL Java_NodeTester_testNode (JNIEnv *jni_env_rptr, 
                                                   jclass java_class) {
    Node *node_rptr = new Node();
    node_rptr->non_virtual_function();
    node_rptr->virtual_function();

    std::cout << node_rptr->get_counter() << std::endl;

    delete node_rptr;
  }
}

NodeTester.java:

public class NodeTester {
  public static native void testNode ();

  static {
    System.loadLibrary("nodetester");
  }

  public static final void main (String[] args) {
    NodeTester.testNode();
  }
}

ожидаемый вывод:

11

фактический вывод с JVM 1.6u23 до 1.6u31:

1

Кажется, что JVM неправильно создает объект "Node" в JNI; хотя возможно, что в этом коде есть что-то неправильное в отношении использования JNI. Когда класс "Node" добавляет к нему больше функциональности (например, больше атрибутов, дополнительных виртуальных и не виртуальных операций), мы можем вызвать ошибку сегментации, а не просто некорректный вывод. Мы компилируем код cpp в 64-битную библиотеку общих ресурсов RedHat с использованием g++ и запускаем код java с 64-разрядной VM сервера. Обратите внимание, что на JVM 1.6u20 до 1.6u22 это дает ожидаемый результат. Я не пробовал более ранние версии.

Мы решили поставить щедрость на этот вопрос! Здесь больше информации о том, что мы уже знаем:

  • JVMs 1.6u22 (и предшествующие) дают ожидаемые результаты
  • Переименование "Node" или помещение его в пространство имен дает ожидаемые результаты
  • Выделение объекта "Node" в стеке вместо кучи в функции JNI дает ожидаемые результаты
  • Нет проблем с не виртуальными компонентами класса "Node"

К сожалению, для нас ни один из этих элементов не приводит к жизнеспособным решениям - "большая проблема", о которой я говорил, заключалась в том, что мы имеем дело с большой существующей базой кода с классом С++ с именем "Node" , который мы необходимо получить доступ через JNI. Мы также попробовали несколько параметров компилятора g++ и javac, а также несколько параметров JVM, но безрезультатно (хотя, если кто-то спотыкается на то, что действительно дает ожидаемые результаты, это было бы приемлемым решением).

4b9b3361

Ответ 1

Хорошо, это не идеальный ответ, но если у нас не будет ничего лучшего, это может помочь следующее. Как поясняется в других комментариях, суть проблемы заключается в двух различных классах С++, названных как Node в глобальном пространстве имен, один из OpenJDK или SunJDK 1.6u23 и, по крайней мере, на RedHat Linux, а другой - другой библиотеки, обе из которых должны иметь свои символы совместно с другими библиотеками. Чтобы загрузить наши символы перед JDK, мы можем установить переменную среды LD_PRELOAD, вызвав например:

LD_PRELOAD=libTheNodeTester.so java ...

Но это может привести к сбою JDK, если он действительно начнет использовать наши символы, как если бы они были из его библиотек...

Ответ 2

При взгляде на код HotSpot есть node.hpp/node.cpp, который объявляет класс node без пространства имен.
Возможно, есть столкновение с чистыми виртуальными функциями.
Мне не хватает знаний VM, чтобы выкапывать все дальше...

Ответ 3

Кажется, что JVM неправильно создает объект "Node" в рамках JNI

Будьте ясны. JVM вообще не создает объект "Node". Среда выполнения С++ делает это.

Я использовал тонны С++ внутри JNI без каких-либо проблем, кроме тех, которые я вызвал.

Первое, что приходит в голову, это то, что вы не проверяете результат "нового" оператора для null. Это не повлияет на не виртуальную функцию, оно просто увидит 'this', который вы не используете, как null, но это предотвратит отправку виртуальной функции, так как косвенная передача через vtable будет вызвана сбоем.

Почему это будет null, это еще один вопрос...

Ответ 4

Для ударов, попробуйте сбросить stdout и stderr перед тем, как выйти из собственного кода. Я думаю, возможно, JVM выходит с данными в каком-то выходном буфере.

Ответ 5

Можно ли добавить слой оболочки в собственный код? т.е. написать класс С++ для прокси-класса класса Node и вызвать его из java вместо прямого вызова Node.

В обертке вы можете импортовать импорт, чтобы избежать двусмысленности (http://www.glenmccl.com/ns_comp.htm, например).