Pergunta

Eu estou usando uma biblioteca de pesquisa que aconselha mantendo busca alça objeto aberto para isso pode beneficiar consulta cache. Ao longo do tempo tenho observado que o cache tende a ficar inchado (algumas centenas de megas e continua crescendo) e Ooms começou a chutar. Não há nenhuma maneira de impor limites deste esconderijo nem plano de quanta memória ele pode usar. Então eu ter aumentado o -Xmx limite, mas isso é apenas uma solução temporária para o problema.

Eventualmente, eu estou pensando em fazer este objeto um referente de java.lang.ref.SoftReference. Então, se o sistema é executado com pouca memória livre, ele iria deixar o objeto ir e um novo será criado sob demanda. Isso diminuiria um pouco de velocidade depois de começar de novo, mas esta é uma alternativa muito melhor do que bater OOM.

O único problema que vejo sobre SoftReferences é que não há nenhuma maneira limpa de obter seus referentes finalizado. No meu caso, antes de destruir o identificador de pesquisa eu preciso fechá-lo, caso contrário o sistema pode ficar sem descritores de arquivos. Obviamente, eu posso quebrar esse identificador em outro objeto, escrever um finalizador sobre ele (ou um gancho em um ReferenceQueue / PhantomReference) e deixar de ir. Mas hey, cada artigo único neste planeta desaconselha o uso de finalizadores e, especialmente, -. Contra finalizadores para liberar identificadores de arquivo (.. Por exemplo, Effective Java ed II, página 27)

Então, eu estou um pouco confuso. Devo ignorar cuidadosamente todos estes conselhos e ir em frente. Caso contrário, existem outras alternativas viáveis? Agradecemos antecipadamente.

EDIT # 1: O texto abaixo foi adicionado depois de testar algum código como sugerido por Tom Hawtin. Para mim, parece que qualquer sugestão não está funcionando ou estou faltando alguma coisa. Aqui está o código:

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++;
   }
}

Se eu executar o trecho acima com -Xmx10m e SoftReferences (como no código acima), eu estou recebendo toneladas de is null :( impressos. Mas se eu substituir o código com MyReference (descomentando duas linhas com MyReference e comentando queridos com SoftReference) Eu sempre obter OOM.

Como eu entendi do conselho, tendo referência duro dentro MyReference não deve impedir objeto bater ReferenceQueue, certo?

Foi útil?

Solução

Toms resposta é a correta, no entanto, o código que foi adicionado para a questão não é o mesmo que o que foi proposto por Tom. O que Tom estava propondo parece mais com isto:

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++;
    }
}

Note-se que a grande diferença é que a referência difícil é o objeto que precisa ser fechado. O objeto circundante pode, e vai, ser lixo coletado, assim você não vai bater o OOM, no entanto você ainda tem a chance de fechar a referência. Uma vez que você sair do loop, que irá também ser lixo coletado. Claro que, no mundo real, você provavelmente não faria res um membro de instância pública.

Dito isto, se você está segurando as referências de arquivos abertos, então você corre um risco muito real de ficar sem os antes de executar fora de memória. Você provavelmente também quer ter um cache LRU para garantir que você não mais manter do que varas de dedo no ar 500 arquivos abertos. Estes também podem ser do tipo MyReference para que eles possam também ser lixo coletado se necessário.

Para esclarecer um pouco sobre como funciona MySoftBloatReference, a classe base, que é SoftReference, ainda mantém a referência ao objeto que está monopolizando toda a memória. Este é o objeto que você precisa para ser libertado para evitar que o OOM aconteça. No entanto, se o objeto é liberado, você ainda precisa liberar os recursos que o inchaço está usando, ou seja, Bloat está usando dois tipos de recursos, memória e um identificador de arquivo, tanto desses recursos precisam ser libertados, ou você corre fora de um ou outro dos recursos. As alças SoftReference a pressão sobre os recursos de memória, libertando esse objeto, mas você também precisa liberar o outro recurso, o identificador de arquivo. Porque Bloat já foi libertado, não podemos usá-lo para liberar o recurso relacionado, assim MySoftBloatReference mantém uma referência difícil o recurso interno que precisa ser fechado. Uma vez que tenha sido informado de que o inchaço foi liberado, ou seja, uma vez que a referência aparece na ReferenceQueue, então MySoftBloatReference também pode fechar o recurso relacionado, através da referência forte que ele tem.

EDIT: Atualizado o código para que ele compila quando jogado em uma classe. Ele usa um StringReader para ilustrar o conceito de como fechar o Reader, que está sendo usado para representar o recurso externo que precisa ser liberado. Neste caso particular fechar esse fluxo é efetivamente um não-op, e por isso não é necessário, mas mostra como fazê-lo se for necessário.

Outras dicas

Para um número finito de recursos: subclasse SoftReference. A referência suave deve apontar para o objecto de inclusão. A forte referência na subclasse deve referenciar o recurso, por isso é sempre fortemente acessível. Ao ler o ReferenceQueue poll o recurso pode ser fechado e removido do cache. As necessidades de cache para ser liberado corretamente (se a si SoftReference é lixo coletado, ele não pode ser enfileirado em um ReferenceQueue).

Tenha cuidado para que você só tem um número finito de recursos não-editado nos o cache - despejar entradas antigas (na verdade, você pode descartar as referências macios com se o cache do finito, que se adequa a sua situação). É geralmente o caso que é o recurso não-memória que é mais importante, caso em que um cache LRU-despejo sem objetos de referência exóticas deve ser suficiente.

(A minha resposta # 1000. Postado de Londres DevDay.)

Ahm.
(Tanto quanto eu sei) Você não pode segurar a vara de ambas as extremidades. Ou você segurar a sua informação, ou você deixá-lo ir.
No entanto ... você pode segurar a algumas informações-chave que lhe permitiria finalizar. Claro, as informações da chave deve ser significativamente menor, em seguida, a "informação real" e não deve ter a informação real em seu gráfico do objeto acessível (referências fracas pode ajudá-lo lá).
Com base no exemplo existente (prestar atenção ao campo de informações de chave):

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");
            }

Edit:
Eu quero elaborar sobre a "quer segurar sua informação ou deixá-lo ir". Supondo que você teve alguma forma de manter a sua informação. Que teria forçado o GC para desmarcar seus dados, fazendo com que os dados para realmente ser limpos apenas depois que você fez com ele, em um segundo ciclo de GC. Isso é possível - e seu exatamente o que finalize () é para. Desde que você disse que você não quer que o segundo ciclo de ocorrer, você não pode manter a sua informação (se a -> b então b -> um!). que significa que você deve deixá-lo ir.

Edit2:
Na verdade, um segundo ciclo iria ocorrer - mas para os seus "dados-chave", não o seu "grande dados inchaço". Os dados reais seriam apuradas no primeiro ciclo.

Edit3:
Obviamente, a verdadeira solução seria usar um segmento separado para a remoção da fila de referência (não fazer poll (), remove (), o bloqueio no segmento dedicado).

@ Paulo - muito obrigado pela resposta e esclarecimento.

@Ran - Eu acho que em sua corrente i código ++ está faltando no final do loop. Além disso, você não precisa fazer rq.remove () no circuito como rq.poll () já remove referência de topo, não é?

Alguns pontos:

1) eu tive que adicionar Thread.sleep (1) declaração após i ++ no circuito (para ambas as soluções de Paul e Ran) para OOM evitar, mas isso é irrelevante para o retrato grande e também é dependente de plataforma. Minha máquina tem um CPU quad-core e está sendo executado Sun Linux 1.6.0_16 JDK.

2) Depois de olhar para essas soluções Acho que vou ficar usando finalizadores. O livro de Bloch fornece seguintes motivos:

  • não há finalizadores de garantia será cumprida prontamente, portanto, nunca fazer qualquer coisa tempo crítico em um finalizador - nem existem quaisquer garantias para SoftRererences
  • Nunca depender de um finalizador para atualizar estado persistente crítico - Eu não sou
  • há uma penalidade de desempenho grave para o uso de finalizadores - Na minha pior caso eu estaria finalizando sobre um único objeto por minuto ou assim. Eu acho que posso viver com isso.
  • uso try / finally - oh sim, eu definitivamente

Tendo necessidade de criar uma enorme quantidade de andaime apenas para o que parece uma tarefa simples não parece razoável para mim. Quero dizer, literalmente, o WTF tarifa por minuto seria muito alto para qualquer outra pessoa olhando para tal código.

3) Saddly, não há nenhuma maneira de pontos de divisão entre Paul, Tom e Ran :( Espero que Tom não se importaria como ele já tem muitos deles :) julgar entre Paulo e Ran foi muito mais difícil - Eu acho que ambas as respostas de trabalho e estão corretas. Eu só estou ajustando aceitar bandeira para a resposta de Paulo, pois foi classificado superior (e tem mais explicação detalhada), mas a solução da Ran não é mau de todo e provavelmente seria a minha escolha se eu escolhi para implementá-lo usando SoftReferences. Obrigado rapazes!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top