7
votes

Fuite de la mémoire Lorsque vous appelez le code Java de C utilisant JNI

J'ai un programme C qui stocke un objet dans le magasin Java à l'aide de JNI. (Avant que quelqu'un demande, utiliser Java Store est une exigence ici et je dois écrire un client dans C qui serait capable d'ajouter et de récupérer des objets de ce magasin).

J'ai fait le programme et essayé d'ajouter 100000 objet de taille. 1kb. Mais après avoir ajouté seulement 50000 objets, je reçois des messages «hors de mémoire» (veuillez noter que je imprime ces messages «hors mémoire» chaque fois que je ne parviens pas à affecter une nouvelle chaîne ou une nouvelle matrice d'octet à l'aide de fonctions NewstringutF et NewbyTeArray). À ce moment-là, ma demande utilise seulement 80 Mo de mémoire. Je ne suis pas pourquoi ces méthodes retournent NULL. Y a-t-il quelque chose qui me manque? P>

En outre, la mémoire continue d'augmenter, même si je libère une matrice d'octet et une chaîne créée pour Java. P>

Voici le code source. P >

    void create_jvm(void)
{
    JavaVMInitArgs vm_args;     
    JavaVMOption vm_options;

    vm_options.optionString = "-Djava.class.path=c:\\Store";
    vm_args.version = JNI_VERSION_1_4;
    vm_args.nOptions = 1;
    vm_args.options = &vm_options;
    vm_args.ignoreUnrecognized = 0;

    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    if(env != null)
    {
        j_store = (*env)->FindClass(env, "com/store");
        if(j_store == null)
        {
            printf("unable to find class. class name: JStore");
        }       
    }   
}

void add(char* key, char* value, int length)
{
    jstring j_key = (*env)->NewStringUTF(env, key);
    jbyteArray j_value = (*env)->NewByteArray(env, length);

    (*env)->SetByteArrayRegion(env, j_value, 0, length, (jbyte *)value);
    ret = (*env)->CallStaticBooleanMethod(env, j_store, method_id, j_key, j_value);

    if(j_value != null)
    {
        (*env)->ReleaseByteArrayElements(env, j_value, (jbyte *)value, 0);
    }
    if(j_key != null)
    {
        (*env)->ReleaseStringUTFChars(env, j_key, key);
    }
}


0 commentaires

4 Réponses :


4
votes

Objet des fonctions DivulgationByTeArrayElements et releasestringTfchars n'est pas de supprimer l'objet mais de le déverrouiller une fois qu'un pointeur a été obtenu avec GetByTeArrayElements ou GetStringUtFchars. Les deux déclarations si les déclarations doivent être supprimées.


4 commentaires

J'ai supprimé ces deux déclarations mais cela n'a fait aucune différence. L'étrange chose est que l'utilisation de la mémoire augmente de 2 Mo à 4 Mo (lors de l'ajout d'un objet 1MB) lorsque j'appelle la méthode Java. On dirait que les matrices d'octets se copient quelque part et que sa jamais déchets recueillis?


Que se passe-t-il dans la méthode Java?


Le code il y a un peu générique. Bien que je passe dans un octet [], la valeur est d'abord sérialisée puis enregistrée sur une hache. Vous voulez que je pose aussi le code Java aussi? (La sérialisation fait partie de l'exigence car les données peuvent toujours être toujours en octet [] et peuvent être stockées sur disque ou dans un autre processus)


Eh bien, c'est la raison pour laquelle deux fois la taille du double de la taille de l'objet est allouée. La collecte des ordures ne se produit que lorsque la taille du tas est atteinte. Il me semble que vous avez besoin de plus d'espace sur le tas. La limite par défaut n'est pas si élevée. Si vous sortez d'une erreur de mémoire à 80 Mo, cela signifie probablement que la taille du tas par défaut est de 64 Mo. Vous pouvez définir le max. Taille de tas avec l'option -xmx.



17
votes

Lorsque vous appelez de nouvelles fonctions ... Fonctions, vous créez une "référence locale" - référence à cet objet dans le cadre de la pile local. Cela empêche Java VM de GC de cet objet pendant que vous en avez encore besoin. C'est bien si vous mettez en place une méthode natale - son cadre local est créé uniquement pour la durée de l'appel de la méthode. Mais lorsque vous créez un objet à partir d'un fil natif Java-attaché, il devient lié à ce cadre de pile de fil, qui sera détruit uniquement avec ce fil.

Ainsi, lorsque vous avez terminé avec un objet, vous pouvez appeler Suppletelocalref () pour vous dire que vous n'en avez plus besoin. Ou vous pouvez entourer la fonction complète () Ajout () avec une paire de pushocalframe () / Poplocalframe () pour créer un cadre local séparé pour sa durée.


4 commentaires

Vous voulez dire quelle que soit la ressource que je reçoive d'utiliser de nouvelles fonctions que je peux relâcher pour GC à l'aide de DELETELOCALREF (). Quelque chose comme ça. jstring j_key = (* env) -> NewstringUF (env, clé); jbytearray j_value = (* env) -> NewbyTearray (env, longueur); .. ... ... (* env) -> Deletelocalref (J_Key); (* env) -> Deletelocalref (J_Value);


Oui, il indique à JNI que c code C est libéré cette ressource - si Java ne l'a pas également de référence, il devient libre pour GC


Quel est le but de relesetringUntfchars et d'autres fonctions de libération? Autant que j'ai lu la documentation, il indique qu'il indique que vous n'avez plus besoin de la ressource et que vous devriez être libéré. Je pense que c'était pour les fonctions indigènes (Java à c) pas l'inverse alors.


Cela semble fonctionner. La mémoire est bien sous contrôle. Et reste environ 80 - 100 Mo. La solution postée par Maurice Perry (ci-dessous) a également fonctionné, mais dans ce cas, la mémoire passe à 500 Mo avant la prise de GC (j'ai défini une taille maximale de tas sur 512 Mo). Mais dans certains cas où j'utilisais un très gros objet (10 Mo), il a parfois renvoyé NULL lorsque j'essaie d'allouer une matrice d'octet. Mais cette solution permet de contrôler la mémoire.



0
votes

Oui, j'ai rencontré le même problème.

Mon application Java Appelez une application C ++ par JNI, l'application C ++ démarrera un nouveau thread et rappelera une méthode Java. Dans le nouveau thread, de nombreux objets ont été créés et la mémoire augmente rapidement, bien que j'utilise Deletelocalref, Poussoir et Poplocalfram.

J'ai trouvé de nombreux objets créés par la méthode NewObject ne peuvent pas être libérés. C'est étrange .


1 commentaires

Nouveauté trouvé, s'il y a une grande chaîne (impression / envoi / passage) dans la méthode de rappel de C ++, la mémoire augmente rapidement, puis débordant plus tard



0
votes

J'ai essayé, vous avez tous dit.

Il faut utiliser la méthode DELETELOCALREF après que n'importe quel JString ait été créé et plus d'utilisation.

newxxx ou CallStaticoBjectMethod peut créer une JString, tout cela doit supprimer.


0 commentaires