78 votes

Passage de pointeurs entre C et Java via JNI

En ce moment, j'essaie de créer une application Java qui utilise la fonctionnalité CUDA. La connexion entre CUDA et Java fonctionne bien, mais j'ai un autre problème et je voulais savoir si mes pensées à ce sujet sont correctes.

Lorsque j'appelle une fonction native depuis Java, je lui passe des données, la fonction calcule quelque chose et renvoie un résultat. Est-il possible de laisser la première fonction renvoyer une référence (pointeur) à ce résultat que je peux passer à JNI et appeler une autre fonction qui effectue d'autres calculs avec le résultat ?

Mon idée était de réduire les frais généraux liés à la copie de données depuis et vers le GPU en laissant les données dans la mémoire du GPU et en passant simplement une référence à celles-ci pour que d'autres fonctions puissent les utiliser.

Après avoir essayé pendant un certain temps, je me suis dit que cela ne devrait pas être possible, car les pointeurs sont supprimés après la fin de l'application (dans ce cas, lorsque la fonction C se termine). Est-ce correct ? Ou suis-je simplement trop mauvais en C pour voir la solution ?

Modifier : Eh bien, pour élargir un peu la question (ou la rendre plus claire) : La mémoire allouée par les fonctions natives JNI est-elle désallouée lorsque la fonction se termine ? Ou puis-je encore y accéder jusqu'à ce que l'application JNI se termine ou que je la libère manuellement ?

Merci pour votre contribution :)

50voto

Denis Tulskiy Points 10444

J'ai utilisé l'approche suivante :

dans votre code JNI, créez une structure qui contiendra les références aux objets dont vous avez besoin. Lorsque vous créez cette structure pour la première fois, renvoyez son pointeur à java en tant qu'objet long . Ensuite, à partir de Java, il suffit d'appeler n'importe quelle méthode avec ceci long en tant que paramètre, et en C, le convertir en un pointeur vers votre structure.

La structure sera dans le tas, donc elle ne sera pas effacée entre les différents appels JNI.

EDIT : Je ne pense pas que vous puissiez utiliser un ptr long = (long)&address; puisque l'adresse est une variable statique. Utilisez-la de la manière suggérée par Gunslinger47, c'est-à-dire en créant une nouvelle instance de classe ou de structure (en utilisant new ou malloc) et en passant son pointeur.

21voto

Dan Berindei Points 2326

En C++, vous pouvez utiliser le mécanisme de votre choix pour allouer/libérer de la mémoire : la pile, malloc/free, new/delete ou toute autre implémentation personnalisée. La seule exigence est que si vous avez alloué un bloc de mémoire avec un mécanisme, vous devez le libérer avec le même mécanisme, donc vous ne pouvez pas appeler free sur une variable de pile et vous ne pouvez pas appeler delete sur malloc de la mémoire.

La JNI possède ses propres mécanismes d'allocation et de libération de la mémoire de la JVM :

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

Ceux-ci suivent la même règle, le seul hic est que les références locales peuvent être supprimées "en masse" soit explicitement, avec PopLocalFrame ou implicitement, lorsque la méthode native se termine.

JNI ne sait pas comment vous avez alloué votre mémoire, donc il ne peut pas la libérer quand votre fonction sort. Les variables de la pile seront évidemment détruites parce que vous écrivez toujours du C++, mais votre mémoire GPU restera valide.

Le seul problème est alors de savoir comment accéder à la mémoire lors des invocations suivantes, et vous pouvez alors utiliser la suggestion de Gunslinger47 :

JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
    MyClass* pObject = new MyClass(...);
    return (long)pObject;
}

JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
    MyClass* pObject = (*pObject)lp;
    ...
}

11voto

Gunslinger47 Points 5166

Java ne saurait pas quoi faire avec un pointeur, mais il devrait être capable de stocker un pointeur à partir de la valeur de retour d'une fonction native, puis de le transmettre à une autre fonction native pour qu'elle s'en occupe. Les pointeurs C ne sont rien d'autre que des valeurs numériques à la base.

Un autre contibuteur devrait vous dire si la mémoire graphique pointée est effacée entre les invocations JNI et s'il existe des solutions de contournement.

10voto

noamtm Points 1748

Je sais que cette question a déjà reçu une réponse officielle, mais j'aimerais ajouter ma solution : Au lieu d'essayer de passer un pointeur, mettez le pointeur dans un tableau Java (à l'index 0) et passez-le à JNI. Le code JNI peut obtenir et définir l'élément du tableau à l'aide de la commande GetIntArrayRegion / SetIntArrayRegion .

Dans mon code, j'ai besoin de la couche native pour gérer un descripteur de fichier (un socket ouvert). La classe Java contient un int[1] et le transmet à la fonction native. La fonction native peut faire ce qu'elle veut avec (get/set) et remettre le résultat dans le tableau.

7voto

Marc Paradise Points 1315

Si vous allouez de la mémoire dynamiquement (sur le tas) à l'intérieur de la fonction native, elle n'est pas supprimée. En d'autres termes, vous êtes en mesure de conserver l'état entre différents appels aux fonctions natives, en utilisant des pointeurs, des variables statiques, etc.

Pensez-y d'une autre manière : que pourriez-vous faire pour conserver en toute sécurité un appel de fonction, appelé depuis un autre programme C++ ? Les mêmes choses s'appliquent ici. Lorsqu'une fonction est quittée, tout ce qui se trouve sur la pile pour cet appel de fonction est détruit ; mais tout ce qui se trouve sur le tas est conservé, sauf si vous le supprimez explicitement.

Réponse courte : tant que vous ne désallouez pas le résultat que vous retournez à la fonction appelante, il restera valide pour une réentrée ultérieure. Assurez-vous simplement de le nettoyer lorsque vous avez terminé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X