Question

J'utilise une bibliothèque de recherche qui conseille de garder l'objet de descripteur de recherche ouvert pour que cela puisse bénéficier au cache de requêtes. Au fil du temps, j'ai constaté que le cache avait tendance à être gonflé (quelques centaines de Mo et que sa taille ne cessait de croître) et que les MOO avaient commencé à entrer en jeu. Il n'y avait aucun moyen d'imposer des limites à ce cache ni de planifier la quantité de mémoire qu'il pouvait utiliser. J'ai donc augmenté la limite Xmx , mais ce n'est qu'une solution temporaire au problème.

Finalement, je pense à faire de cet objet un référent de java.lang.ref.SoftReference . Ainsi, si le système manque de mémoire libre, l'objet sera laissé et un nouvel objet sera créé à la demande. Cela réduirait un peu la vitesse après un nouveau départ, mais il s'agit d'une bien meilleure alternative que de frapper le MOO.

Le seul problème que je vois à propos de SoftReferences est qu’il n’existe aucun moyen propre de finaliser leurs référents. Dans mon cas, avant de détruire le descripteur de recherche, je dois le fermer, sinon le système risque de manquer de descripteurs de fichiers. Évidemment, je peux envelopper cette poignée dans un autre objet, y écrire un finaliseur (ou y accrocher une RéférenceQueue / PhantomReference) et la laisser partir. Mais, hé, chaque article de cette planète déconseille l’utilisation des finaliseurs, et plus particulièrement des finaliseurs pour libérer les descripteurs de fichiers (par exemple, Effective Java éd. II, page 27.).

Je suis donc un peu perplexe. Dois-je soigneusement ignorer tous ces conseils et continuer. Sinon, y a-t-il d'autres alternatives viables? Merci d'avance.

EDIT # 1: Le texte ci-dessous a été ajouté après avoir testé du code suggéré par Tom Hawtin. Pour moi, il semble que la suggestion ne fonctionne pas ou que quelque chose me manque. Voici le code:

class Bloat {  // just a heap filler really
   private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;

   private final int ii;

   public Bloat(final int ii) {
      this.ii = ii;
   }
}

// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
   private final T hardRef;

   MyReference(T referent, ReferenceQueue<? super T> q) {
      super(referent, q);
      this.hardRef = referent;
   }
}

//...meanwhile, somewhere in the neighbouring galaxy...
{
   ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
   Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
   int i=0;

   while(i<50000) {
//      set.add(new MyReference<Bloat>(new Bloat(i), rq));
      set.add(new SoftReference<Bloat>(new Bloat(i), rq));

//      MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll();
      SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll();

      if (polled != null) {
         Bloat polledBloat = polled.get();
         if (polledBloat == null) {
           System.out.println("is null :(");
         } else {
           System.out.println("is not null!");
         }
      }
      i++;
   }
}

Si je lance l'extrait de code ci-dessus avec -Xmx10m et SoftReferences (comme dans le code ci-dessus), des tonnes de sont nuls: ( imprimé. Mais si je remplacez le code par MyReference (en supprimant la mise en commentaire de deux lignes avec MyReference et en les commentant avec SoftReference), je reçois toujours le MOO.

Comme je l’ai compris dans les conseils, avoir une référence précise dans MyReference ne devrait pas empêcher les objets d’être frappés par ReferenceQueue , non?

Était-ce utile?

La solution

La réponse de Toms est la bonne, mais le code ajouté à la question n’est pas le même que celui proposé par Tom. Ce que Tom proposait ressemble plus à ceci:

class Bloat {  // just a heap filler really
    public Reader res;
    private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;

    private final int ii;

    public Bloat(final int ii, Reader res) {
       this.ii = ii;
       this.res = res;
    }
 }

 // as recommended by Tom Hawtin
 class MySoftBloatReference extends SoftReference<Bloat> {
    public final Reader hardRef;

    MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) {
       super(referent, q);
       this.hardRef = referent.res;
    }
 }

 //...meanwhile, somewhere in the neighbouring galaxy...
 {
    ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
    Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
    int i=0;

    while(i<50000) {
        set.add(new MySoftBloatReference(new Bloat(i, new StringReader("test")), rq));

        MySoftBloatReference polled = (MySoftBloatReference) rq.poll();

        if (polled != null) {
            // close the reference that we are holding on to
            try {
                polled.hardRef.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        i++;
    }
}

Notez que la grande différence réside dans le fait que la référence dure est l’objet à fermer. L'objet environnant peut et sera récupéré, de sorte que vous ne toucherez pas le MOO, mais vous aurez toujours une chance de fermer la référence. Une fois que vous aurez quitté la boucle, les déchets seront également collectés. Bien entendu, dans le monde réel, vous ne feriez probablement pas de res un membre d'instance publique.

Cela dit, si vous conservez des références de fichiers ouvertes, vous courez un risque très réel de manquer de celles-ci avant de manquer de mémoire. Vous souhaiterez probablement également disposer d’un cache LRU pour ne pas laisser plus de doigts collés dans les airs 500 fichiers ouverts. Ceux-ci peuvent également être de type MyReference afin qu’ils puissent également être ramassés au besoin.

Pour clarifier un peu le fonctionnement de MySoftBloatReference, la classe de base, SoftReference, contient toujours la référence à l'objet qui monopolise toute la mémoire. C'est l'objet que vous devez libérer pour éviter que le MOO ne se produise. Toutefois, si l'objet est libéré, vous devez toujours libérer les ressources utilisées par Bloat, c'est-à-dire que Bloat utilise deux types de ressources, la mémoire et un descripteur de fichier. Ces deux ressources doivent être libérées. sur l'une ou l'autre des ressources. SoftReference gère la pression exercée sur la ressource de mémoire en libérant cet objet. Toutefois, vous devez également libérer l'autre ressource, le descripteur de fichier. Bloat ayant déjà été libéré, nous ne pouvons pas l'utiliser pour libérer la ressource associée. MySoftBloatReference conserve donc une référence fixe à la ressource interne à fermer. Une fois qu'il a été informé que le bloat a été libéré, c'est-à-dire une fois que la référence est apparue dans la référence, alors MySoftBloatReference peut également fermer la ressource associée, par le biais de la référence dure dont elle dispose.

EDIT: Mise à jour du code afin qu'il soit compilé lorsqu'il est jeté dans une classe. Il utilise un StringReader pour illustrer le concept de la fermeture du Reader, utilisé pour représenter la ressource externe devant être libérée. Dans ce cas particulier, la fermeture de ce flux est en réalité une opération sans issue et n'est donc pas nécessaire, mais elle montre comment le faire si nécessaire.

Autres conseils

Pour un nombre fini de ressources: Sous-classe SoftReference . La référence souple doit pointer sur l'objet englobant. Une référence forte dans la sous-classe doit faire référence à la ressource, de sorte qu'elle soit toujours hautement accessible. Lors de la lecture dans ReferenceQueue poll , la ressource peut être fermée et supprimée du cache. Le cache doit être libéré correctement (si un SoftReference est lui-même collecté, il ne peut pas être mis en file d'attente sur une ReferenceQueue ).

Veillez à ne conserver qu'un nombre fini de ressources non libérées dans le cache - évacuez les anciennes entrées (en effet, vous pouvez supprimer les références temporaires avec le cache if, si cela convient à votre situation). C’est généralement la ressource la plus importante qui compte le plus, auquel cas un cache d’expulsion de LRU sans objet de référence exotique devrait suffire.

(Ma réponse n ° 1000. Publié par London DevDay.)

Ahm.
(Autant que je sache) Vous ne pouvez pas tenir le bâton des deux côtés. Soit vous tenez à vos informations, soit vous les laissez partir.
Cependant ... vous pouvez conserver certaines informations clés qui vous permettraient de finaliser. Bien entendu, les informations clés doivent être nettement plus réduites que les "informations réelles". et ne doit pas avoir les informations réelles dans son graphe d'objet accessible (de faibles références pourraient vous aider là-bas).
En vous appuyant sur l'exemple existant (faites attention au champ d'informations clés):

public class Test1 {
    static class Bloat {  // just a heap filler really
        private double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;

        private final int ii;

        public Bloat(final int ii) {
            this.ii = ii;
        }
    }

    // as recommended by Tom Hawtin
    static class MyReference<T, K> extends SoftReference<T> {
        private final K keyInformation;

        MyReference(T referent, K keyInformation, ReferenceQueue<? super T> q) {
            super(referent, q);
            this.keyInformation = keyInformation;
        }

        public K getKeyInformation() {
            return keyInformation;
        }
    }

    //...meanwhile, somewhere in the neighbouring galaxy...
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
        Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
        int i = 0;

        while (i < 50000) {
            set.add(new MyReference<Bloat, Integer>(new Bloat(i), i, rq));

            final Reference<? extends Bloat> polled = rq.poll();

            if (polled != null) {
                if (polled instanceof MyReference) {
                    final Object keyInfo = ((MyReference) polled).getKeyInformation();
                    System.out.println("not null, got key info: " + keyInfo + ", finalizing...");
                } else {
                    System.out.println("null, can't finalize.");
                }
                rq.remove();
                System.out.println("removed reference");
            }

Modifier:
Je voudrais élaborer sur le "ou détenir vos informations ou laisser-le aller". En supposant que vous ayez un moyen de conserver vos informations. Cela aurait obligé le GC à annuler la marque de vos données, ce qui aurait pour effet de les nettoyer uniquement une fois que vous en auriez terminé, dans un deuxième cycle de GC. C'est possible - et c'est exactement ce à quoi finalize () est destiné. Puisque vous avez déclaré que vous ne souhaitiez pas que le deuxième cycle se produise, vous ne pouvez pas conserver vos informations (si a - > b alors! B - >! A). ce qui signifie que vous devez laisser tomber.

Edit2:
En fait, un deuxième cycle se produirait - mais pour vos "données de clé", pas pour vos "données de masse excessive". Les données réelles seraient effacées au premier cycle.

Edit3:
De toute évidence, la vraie solution utiliserait un thread séparé pour la suppression de la file d'attente des références (ne pas interroger (), remove (), bloquer sur le thread dédié).

@Paul - merci beaucoup pour la réponse et la clarification.

@Ran - Je pense que dans votre code actuel, i ++ manque à la fin de la boucle. En outre, vous n'avez pas besoin de faire rq.remove () dans la boucle car rq.poll () supprime déjà la référence la plus élevée, n'est-ce pas?

Quelques points:

1) Je devais ajouter l'instruction Thread.sleep (1) après i ++ dans la boucle (pour les deux solutions de Paul et de Ran) afin d'éviter OOM, mais cela n'a aucune incidence sur la vue d'ensemble et dépend également de la plate-forme. Ma machine a un processeur quad-core et exécute Sun Linux 1.6.0_16 JDK.

2) Après avoir examiné ces solutions, je pense que je vais utiliser des finaliseurs. Le livre de Bloch fournit les raisons suivantes:

  • il n’existe aucune garantie que les finaliseurs soient exécutés rapidement; par conséquent, ne réalisez jamais une tâche cruciale dans un finaliseur - ni de garantie pour SoftRererences!
  • Ne comptez jamais sur un finaliseur pour mettre à jour un état persistant critique - Je ne suis pas
  • l’utilisation des finaliseurs est très pénalisante en termes de performances. Dans le pire des cas, je finaliserais environ un objet par minute. Je pense que je peux vivre avec ça.
  • utiliser essayer / enfin - oh oui, je le ferai certainement!

Avoir la nécessité de créer une énorme quantité d’échafaudages juste pour ce qui semble une tâche simple ne me semble pas raisonnable. Je veux dire, littéralement, le tarif WTF par minute serait assez élevé pour quiconque regarde un tel code.

3) Malheureusement, il n'y a aucun moyen de diviser des points entre Paul, Tom et Ran :( J'espère que cela ne dérangerait pas Tom, car il en avait déjà beaucoup :) Juger entre Paul et Ran était beaucoup plus difficile. Je pense que les deux réponses fonctionnent et sont correctes. Je ne fais que mettre drapeau d'acceptation sur la réponse de Paul parce qu'elle a été mieux notée (et a une explication plus détaillée), mais la solution de Ran n'est pas mal du tout et serait probablement mon choix si j'avais choisi de l'implémenter en utilisant SoftReferences. Merci les gars!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top