Greg's Devblog Par un développeur, pour les développeurs

3sept/100

Empilement d’appels…

Dans le reader qu'on fait, quand je veux ajouter une fonctionnalité (ou juste une méthode à la con) dans la couche du reader, voilà ce qu'il faut ajouter (j'ai mis en rouge les méthodes C/C++, en orange les méthodes Java "génériques", et en vert la partie Android)

  • La méthode dans l'objet C++ qui s'occupe de faire le rendu (normal)
  • La méthode virtuelle dans la classe abstraite de type Reader
  • La méthode JNI qui appelle la méthode sur un objet de type Reader
  • La méthode Java dans la classe Reader qui utilise le code JNI
  • La méthode Java dans l'interface IReader (pour que tout le monde puisse voir la méthode précédemment ajoutée...)
  • La méthode Java dans la classe Presenter (car on n'accède pas directement au Reader)
  • La méthode qui utilise la méthode du Presenter dans le code, côté Android (enfin !)

Du coup quand je fais ça je réfléchis bien à si j'ai plusieurs méthodes à ajouter, car... La partie en rouge est sous Eclipse sous Linux (et se compile en ligne de commande), la partie en orange sous Netbeans sous Linux, et la partie en vert sous Eclipse sous MacOSX ^^.

30août/100

JNI : trop d’objets tue l’objet

Un charmant petit plantage sur Android tout à l'heure, alors que le même code marchait très bien sur d'autres JVM (celle de Sun Oracle, ainsi que JamVM). Heureusement, le message d'erreur de Dalvik est très explicite, et m'indique que côté JNI je dépasse le nombre autorisé de local refs (qui est de seulement 512).

En fait dans ce bout de code je génère une table des matières en Java à partir d'une table des matières en C++. Le souci, c'est que celle-ci possède genre 300 entrées, et qu'en Java pour chaque entrée je dois créer 1 objet et 1 String, soit 2 objets au total. 300*2 = 600 > 512... Game Over !

La solution est simple, et les explications sur le pourquoi du comment très clairement exposées sur le site de Sun :

(*env)->DeleteLocalRef(env, jstr);

Une fois que l'objet créé a été refilé comme référence à un autre objet Java, on n'a plus vraiment besoin de garder la référence locale (qui in fine ne sert qu'à s'assurer que le GC ne va pas venir libérer des objets qu'on vient d'allouer côté C...), et donc on peut supprimer la LocalRef.

Taggé comme: , , , Aucun commentaire
22août/100

JVM : plusieurs implémentations = plusieurs problèmes

L'open source, c'est génial. Java, c'est fantastique, c'est la liberté, tout le monde peut faire ce qu'il veut avec. Mais si chacun est libre de faire sa JVM, ça veut aussi dire qu'il existe un risque accru de problèmes.

C'était déjà le cas avec le Java sur mobiles, à l'époque des "vieux" Nokia et compagnie : chaque téléphone avait son lot de bugs inclus dans la JVM, et les développeurs devaient faire avec. Du coup, au lieu d'avoir une seule version d'un code qui marche partout (puisque c'était le principe du Java...), il fallait à l'époque être capable de patcher les différentes versions pour les différents téléphones... Pratique !

Cette semaine on a eu ce genre de problèmes, mais en utilisant des trucs standards : la JVM JamVM (qu'on utilise pour sa performance et sa compacité en mémoire) et la version JDBC/JNI de SQLite : avec la JVM de Sun, ça marche parfaitement, tandis qu'avec JamVM, on a un segmentation fault. C'est le genre d'erreur un peu gênant quand on fait du Java, puisque ce n'est pas sensé se produire ^^ Mais c'est l'utilisation de JNI, et donc de code natif, qui introduit ce genre de soucis.

Heureusement (façon de parler), le bug ne se produisait pas sur toutes les requêtes, et on s'est rendu compte qu'il ne se produisait qu'en cas de requête qui aurait du renvoyer un String à null... En mettant les mains dans le code de la couche JNI, en remontant les appels successifs, on a rapidement cerné le soucis :

JNIEXPORT jstring JNICALL Java_org_sqlite_NativeDB_column_1text(JNIEnv *env, jobject this, jlong stmt, jint col)
{
    return (*env)->NewStringUTF(env, (const char*)sqlite3_column_text(toref(stmt), col));
}

Ce petit bout de code fait le pont entre la partie purement C de sqlite (qui renvoie donc un (char *)) et la partie JNI (qui doit renvoyer un String) : on créé un String à partir du (char *) en utilisant les routines fournies par la JVM... Et c'est là que tout s'explique : la JVM de Sun autorise qu'on lui file un pointeur à NULL, et renvoie alors null comme String, alors que JamVM ne l'autorise pas.

On a été assez étonné de tomber sur un problème comme ça (à la fois du côté de JamVM qui ne gère pas ça comme la JVM de Sun, et du côté de sqlitejdbc, qui pourrait jouer la sécurité en faisant la vérification à la main). Le patch a été facile, il suffit de rajouter un petit if...

Moralité ? On ne peut pas toujours faire confiance aux libs et outils qu'on utilise, parfois il faut savoir s'en méfier et mettre le nez dedans :-)

9juil/100

Java : fuites mémoires ??

Java, c'est bien, on ne peut pas avoir de fuites mémoires, puisqu'on a un Garbage Collector (GC)... En êtes-vous sûrs ?

Je pose cette question toute conne parce qu'au boulot, dans l'appli Java, on plante au bout d'un moment par manque de mémoire ^^ Alors ça peut paraitre bizarre dit comme ça, mais j'ai commencé à me documenter un peu sur le pourquoi du comment, et je trouve ça assez intéressant.

Taggé comme: , , , , Lire la suite