JVM не работает должным образом с кодом JNI C ++, содержащим класс с именем "Node" - PullRequest
24 голосов
/ 09 марта 2012

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

ОБНОВЛЕНИЕ: слегка изменил пример, чтобы сосредоточить внимание на проблеме, которую «virtual_function ()», по-видимому, не вызывается.

ОБНОВЛЕНИЕ:Упрощенный пример еще больше, основанный на текущих комментариях.

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.Когда класс «Узел» получает больше функциональности (например, больше атрибутов, дополнительные виртуальные и не виртуальные операции), мы можем вызвать ошибку сегментации, а не просто неправильный вывод.Мы компилируем код cpp в 64-разрядную библиотеку RedHat Linux с использованием g ++ и запускаем код Java с 64-разрядной виртуальной машиной сервера.Обратите внимание, что на JVM 1.6u20 - 1.6u22 это дает ожидаемый результат.Я не пробовал более ранние версии.

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

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

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

Ответы [ 5 ]

7 голосов
/ 20 марта 2012

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

LD_PRELOAD=libTheNodeTester.so java ...

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

6 голосов
/ 19 марта 2012

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

2 голосов
/ 09 марта 2012

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

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

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

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

Почему это будет ноль - это другой вопрос ...

1 голос
/ 13 марта 2012

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

0 голосов
/ 20 марта 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...