Modèle de mémoire J2ObjC

Ce document décrit la gestion de la mémoire sous le code traduit J2ObjC et le comportement des programmes lors de l'accès à la mémoire partagée.

Gestion de la mémoire

L'un des objectifs de J2ObjC est de produire du code traduit qui s'intégrera parfaitement à l'environnement de comptabilisation de référence d'Objectif-C. Cela facilite l'utilisation du code Java traduit à partir d'Goal-C écrit en natif, car il n'y a pas de transfert de propriété maladroit pour les objets transmis entre les environnements Java et Objectif-C.

Étant donné que Java utilise la récupération de mémoire pour la gestion de la mémoire, le code Java ne contient pas de gestion explicite de la mémoire de ses objets. J2ObjC doit donc insérer les appels de comptage de références de manière appropriée pour garantir que les objets sont libérés au bon moment. Nous nous sommes décidés sur l'ensemble de règles suivant, que nous avons trouvé à la fois performant et pratique:

  • Tous les objets seront actifs pendant au moins la durée du pool de libération automatique actuel.
    • Cette règle générale nous permet d'ignorer de nombreuses règles de conservation et libérations qui, dans le cas contraire, seraient nécessaires.
  • Les variables locales ne sont pas conservées.
    • Il n'y a pas de référence pour le comptage des appels lors des lectures ou des écritures de variables locales.
  • Les champs sont conservés.
    • L'attribution d'un appel de champ conserve la nouvelle valeur et libère automatiquement l'ancienne valeur.
  • Les nouveaux objets sont immédiatement libérés de manière automatique. (sauf si elles sont immédiatement attribuées à un champ)

Cycles de référence

Un cycle de référence existe lorsqu'un objet fait référence à lui-même, directement ou indirectement par le biais de ses champs. Les cycles de référence peuvent être nettoyés par la récupération de mémoire de Java, mais ils entraînent une fuite de mémoire dans l'environnement de comptage de référence d'Objectif-C. Il n'existe aucun moyen automatisé d'empêcher les cycles de référence de se produire. Toutefois, nous fournissons un outil de localisation de cycles qui automatise la détection des cycles. Voici quelques méthodes courantes pour corriger un cycle de référence:

  • Ajout d'une annotation @Weak ou @WeakOuter pour affaiblir l'une des références.
  • Ajoutez une méthode cleanup() à l'un des objets qui définit certains champs sur "null". Appelez cleanup() avant de supprimer l'objet.
  • Repensez le code pour éviter complètement de créer un cycle de référence.

Mémoire partagée

Dans un programme multithread, certaines données peuvent être partagées par plusieurs threads. Java fournit plusieurs outils pour permettre un accès thread-safe aux données partagées. Cette section décrit la prise en charge de J2ObjC pour accéder aux données partagées.

Synchronisé

J2ObjC mappe le mot clé synchronized directement sur Objectif-C @synchronized.

Atomicité

Java garantit l'atomicité pour les chargements et les magasins de tous types, à l'exception de long et double. Consultez JLS-17.7. À l'exception des champs volatile (décrits ci-dessous), J2ObjC ne fournit aucun traitement spécial pour garantir les chargements et les magasins atomiques. Ce qui implique ce qui suit:

  • Étant donné que toutes les plates-formes iOS sont au format 32 ou 64 bits, les chargements et les magasins de types primitifs, à l'exception de long et double, sont atomiques sur les appareils 32 bits, et tous sont atomiques sur les systèmes 64 bits.
  • Les chargements et les magasins de types d'objets ne sont pas atomiques dans J2ObjC.
    • La mise à jour atomique du nombre de références est trop coûteuse.
    • Un champ d'objet peut être rendu atomique en le déclarant volatile. (voir ci-dessous).

Champs volatils

Pour les champs volatile, Java fournit à la fois l'atomicité et un ordre cohérent sur le plan séquentiel (JLS-8.3.1.4), qui peuvent être utilisés pour la synchronisation. J2ObjC fournit les mêmes garanties que Java pour tous les champs volatile. J2ObjC utilise les mécanismes suivants pour les champs volatile:

  • Les types primitifs sont mappés avec les types atomiques c11.
    • par exemple volatile int -> _Atomic(jint)
  • Les champs d'objet sont protégés par des verrous mutex pthread. (Impossible d'utiliser les verrouillages automatiques en raison de l'inversion des priorités.)
    • L'exclusion mutuelle est nécessaire pour éviter les conditions de concurrence avec le comptage des références.
    • L'implémentation est très similaire aux propriétés atomiques Objectif-C.

Types atomiques

Java fournit un certain nombre de types atomiques dans le package java.util.concurrent.atomic. Elles sont toutes entièrement prises en charge dans J2ObjC avec des implémentations personnalisées.

Champs finaux

Java garantit qu'un thread verra les valeurs initialisées pour les champs finaux d'un objet sans nécessiter de synchronisation lors du partage de l'objet. (JSL-17.5) Toutefois, comme J2ObjC n'accepte pas l'accès atomique aux types d'objets non volatiles (voir ci-dessus), il n'existe aucun moyen sûr de partager un objet sans synchronisation. Par conséquent, aucune contrainte supplémentaire n'est appliquée à l'utilisateur J2ObjC en omettant les limites de mémoire nécessaires pour les champs finaux.