Por el momento, estoy tratando de crear un Java de la aplicación que utiliza CUDA-funcionalidad. La conexión entre CUDA y Java funciona bien, pero tengo otro problema, y quería preguntar, si mis pensamientos son correctos.

Cuando llamo a una función nativa de Java, te paso algunos datos para que, las funciones calcula algo y devuelve un resultado. Es posible que, para que la primera función devuelve una referencia (puntero) a este resultado, que me puede pasar a la JNI y llamar a otra función que hace más cálculos con el resultado?

Mi idea era reducir la carga que proviene de la copia de datos a y desde la GPU dejando los datos en la memoria de la GPU y de paso una referencia para otras funciones que puede utilizar.

Después de probar algún tiempo, pensé para mí, esto no debería ser posible, ya que los punteros se eliminan después de los extremos de la aplicación (en este caso, cuando el C-función termina). Es esto correcto? O solo estoy a la mala en C para ver la solución?

Editar:
Además, para ampliar la pregunta un poco (o hacerlo más claro): Es la memoria asignada por JNI funciones nativas removidos cuando la función termina? O puedo acceder a ella hasta que la JNI aplicación termina o cuando me libre de hacerlo manualmente?

Gracias por tu aporte 🙂

InformationsquelleAutor Volker | 2009-10-27

7 Comentarios

  1. 44

    He utilizado el siguiente enfoque:

    en su JNI de código, crear una estructura que contienen referencias a los objetos que usted necesita. Al crear por primera vez esta estructura, devolver su puntero a java como un long. Luego, a partir de java que acaba de llamar a cualquier método con este long como parámetro, y en C cast a un puntero a la estructura.

    La estructura será en el montón, por lo que no se borrará entre diferentes llamadas JNI.

    EDIT: no creo que usted puede usar por mucho tiempo ptr = (long)&address; desde la dirección de una variable estática. Utilizarlo de la manera Gunslinger47 sugerido, es decir, crear una nueva instancia de la clase o struct (utilizando nuevos o malloc) y pasar su puntero.

    • Que es como yo lo hice ahora tengo que enviar el puntero a java como un largo y pase a C. Pero, ¿cómo puedo decir que el tiempo que acaba de recibir una dirección? tiempo ptr = (long)&dirección; Eso es lo que he intentado, pero no tengo el valor que debe ser en la dirección, solo me dan la dirección de sí mismo (o algunas otras direcciones, pero no el valor de 🙁 )
    • …debería ser asteriscos antes de ptr y después el segundo tiempo…
    • MyClass *pObject = ...; long lp = (long)pObject; pObject = (*pObject)lp;
    • El uso de largo en x32 y x64 plataformas? Me alegro de que no vamos a trasladar a la 128 bits de las máquinas en cualquier momento pronto…
    • Hola @Denis Tulskiy , tengo una pregunta acerca de la conversión de un largo puntero en java para una matriz puedo usar itk en el mismo concepto y tengo un puntero a los datos de la imagen, pero no sé cómo conseguir el array desde el puntero. traté de usar com.sol.jna.Puntero pero no sabía que gow para hacer puntos a los mismos datos que se pasa el puntero de referencia.
    • Comparto @dhardy la preocupación. Esta solución no es portable – no hay ninguna garantía de que long será lo suficientemente grande como para contener un puntero.
    • Denis puede usted por favor, escriba un pseudo/código de ejemplo para entender esto. También lo que sobre el objeto de la clase en lugar de una estructura, es esto posible! Estoy siguiendo la este. Por favor, sugiera.
    • sun.misc.unsafe hace fuerte uso de longs como punteros, por lo que es efectivamente compatible, independientemente de Oracle en realidad se aprueba… por lo tanto, acepte el contrato a su propio riesgo.
    • También comparto @dhardy la preocupación. Una forma sería la de pasar una matriz de bytes de java, donde cada byte de la matriz es de 8 bits del puntero de valor (y hacer que la matriz de longitud diferente dependiendo del sistema). Sin embargo, esto significa que usted también tendría que pasar el tamaño del puntero a la función JNI.
    • en ese caso creo que es mejor tener un tamaño de clave para sus datos. Mantener un hash-mapa de clave y puntero de retorno de la clave de código nativo de java.

  2. 15

    En C++ se puede utilizar cualquier mecanismo que desea asignar/libera la memoria: el stack, malloc/free, nuevo, eliminar o cualquier otra implementación personalizada. El único requisito es que, si usted asigna un bloque de memoria con un solo mecanismo, tiene conexión con el mismo mecanismo, de modo que usted no puede llamar a free en una pila variable y no se puede llamar delete en malloced memoria.

    JNI tiene sus propios mecanismos para asignar y liberar memoria de JVM:

    • NewObject/DeleteLocalRef
    • NewGlobalRef/DeleteGlobalRef
    • NewWeakGlobalRef/DeleteWeakGlobalRef

    Estos siguen la misma regla, la única pega es que los árbitros pueden ser eliminados «en masa», ya sea de forma explícita, con PopLocalFrame, o de manera implícita, cuando el método nativo salidas.

    JNI no sabe cómo se asigna la memoria, por lo que no puede liberarse cuando la función termina. Pila de variables, obviamente, será destruido porque usted todavía está escrito en C++, pero su memoria de la GPU será válido.

    El único problema es entonces cómo acceder a la memoria en las siguientes llamadas y, a continuación, puede utilizar Gunslinger47 sugerencia:

    JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
        MyClass* pObject = new MyClass(...);
        return (long)pObject;
    }
    
    JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
        MyClass* pObject = (MyClass*)lp;
        ...
    }
  3. 10

    Java no sabría qué hacer con un puntero, pero debe ser capaz de almacenar un puntero de un nativo de la función de devolución de valor, a continuación, entregar a otra función nativa para tratar con. C punteros no son nada más que los valores numéricos en el núcleo.

    Otro contibutor tengo que decir que si o no el señalado para los gráficos de la memoria se borra entre JNI invocaciones y si hubiera work-arounds.

    • Sobre tu segundo párrafo: La única cosa a tener en cuenta es asegurarse de que cualquier memoria asignada obtiene desasignado así. El enfoque recomendado es tener algún tipo de cierre/método dispose() en el objeto de la celebración de la referencia. los finalizadores son tentadores, pero vienen con un par de inconvenientes lo que vale la pena evitarlos si es posible.
    • No debería haber escrito «la única cosa» por cierto… JNI está lleno de fosas en el que caer.
    • Yo ya incluye funciones para liberar la memoria asignada, de manera que si no hay problema 🙂 El principal problema sigue siendo: ¿la memoria de la estancia asignada si hago neto libre de ella? Quiero decir, incluyendo las direcciones y los valores…sé que voy a tener pérdidas de memoria, entonces, en si yo no lo hago, así que ya incluido 😉
    • Depende de cómo asignar. La única excepción a la regla de que usted tiene para desasignar/liberar la memoria es normalmente si algo se almacena en la pila. Si es así, va a ser «liberado» cuando el puntero de pila se mueve hacia atrás en la salida de una función. Así que si usted tiene la memoria asignada utilizando algún tipo de función de asignación de memoria (excepto para «ubica») tendrás que liberarlo. Es que realmente no están en absoluto relacionadas con java. Tan pronto como usted ha hecho la JNI saltar las reglas son de la C mundo a menos que utilice objetos java.
    • Fredrik, su respuesta es correcta, y que realmente debería ser trasladado a su propia respuesta para el crédito.
    • «C punteros no son nada más que long valores en el núcleo.» – No hay nada en el estándar de C que dice eso, y esta es la invocación de un comportamiento indefinido.

  4. 7

    Sé que esta pregunta ya fue oficialmente respondió, pero me gustaría añadir mi solución:
    En lugar de tratar de pasar un puntero, coloque el puntero en una Java de la matriz (en el índice 0) y que pase a JNI. JNI código puede obtener y establecer el elemento de la matriz utilizando GetIntArrayRegion/SetIntArrayRegion.

    En mi código, necesito el nativo de la capa de gestionar un descriptor de archivo (open socket). La clase Java que contiene una int[1] matriz y se lo pasa a la función nativa. La función nativa puede hacer cualquier cosa con él (get/set) y volver a poner el resultado en la matriz.

    • ¿Cómo convertir un transferidos a largo puntero a un array en java
  5. 6

    Si usted está asignar memoria dinámicamente (en el montón) dentro de la función nativa, no se elimina. En otras palabras, usted es capaz de mantener el estado entre las diferentes llamadas a funciones nativas, el uso de punteros, estático de vars, etc.

    Pensar de una manera diferente: ¿qué se puede hacer de forma segura en una llamada de función, llamada desde otro programa de C++? Lo mismo se aplica aquí. Cuando una función se sale, nada en la pila para que la llamada a la función es destruida; pero nada en el montón se mantiene a menos que explícitamente eliminarlo.

    Respuesta corta: mientras no desasignar el resultado que estamos volviendo a la función de llamada, seguirá siendo válida para la re-entrada posterior. Sólo asegúrese de limpiar cuando haya terminado.

  6. 6

    Mientras que el aceptado la respuesta de @denis-tulskiy tiene sentido, yo personalmente he seguido las sugerencias de aquí.

    Así que en lugar de usar un pseudo-tipo de puntero como jlong (o jint si quieres ahorrar algo de espacio en 32bits arco), utilice en su lugar un ByteBuffer. Por ejemplo:

    MyNativeStruct* data; //Initialized elsewhere.
    jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct));

    que luego puede volver a utilizar con:

    jobject bb; //Initialized elsewhere.
    MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb);

    Muy simple de los casos, esta solución es muy fácil de usar. Supongamos que usted tiene:

    struct {
      int exampleInt;
      short exampleShort;
    } MyNativeStruct;

    Por el lado de Java, usted simplemente tiene que hacer:

    public int getExampleInt() {
      return bb.getInt(0);
    }
    
    public short getExampleShort() {
      return bb.getShort(4);
    }

    Que le salva de la escritura muchas de código repetitivo ! Uno, sin embargo, debería prestar atención a la orden de bytes como se explica aquí.

  7. 0

    Su mejor hacer esto exactamente cómo Inseguro.allocateMemory hace.

    De crear el objeto, a continuación, escriba a (uintptr_t), que es una de 32/64 bits entero sin signo.

    return (uintptr_t) malloc(50);
    
    void * f = (uintptr_t) jlong;

    Esta es la única forma correcta de hacerlo.

    Aquí está la cordura comprobación de Seguros.allocateMemory hace.

    inline jlong addr_to_java(void* p) {
      assert(p == (void*)(uintptr_t)p, "must not be odd high bits");
      return (uintptr_t)p;
    }
    
    UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size))
      UnsafeWrapper("Unsafe_AllocateMemory");
      size_t sz = (size_t)size;
      if (sz != (julong)size || size < 0) {
        THROW_0(vmSymbols::java_lang_IllegalArgumentException());
      }
      if (sz == 0) {
        return 0;
      }
      sz = round_to(sz, HeapWordSize);
      void* x = os::malloc(sz, mtInternal);
      if (x == NULL) {
        THROW_0(vmSymbols::java_lang_OutOfMemoryError());
      }
      //Copy::fill_to_words((HeapWord*)x, sz /HeapWordSize);
      return addr_to_java(x);
    UNSAFE_END
    • Esto es no el estándar de la definición de uintptr_t. Es una muy mala idea hacer eso. Se define como siendo lo suficientemente grande como para contener cualquier puntero, y puede ser cualquier longitud que esto requiere. Normalmente en un sistema de 64 bits esto sería de 64 bits, pero la norma no incluso permitir a esa suposición. Nunca se debe hacer suposiciones sobre el tamaño de uintptr_t.
    • Esta es la forma en la JVM asigna memoria en 32 y 64 bits de los sistemas. Asignación de uintptr_t permite una limpieza de la conversión de un jlong. No voy a debatir si es un buen camino o no, pero es la forma en que la JVM hace.

Dejar respuesta

Please enter your comment!
Please enter your name here