Pessoalmente, não deparei com uma situação em que precisei usar o tipo WeakReference no .Net, mas a crença popular parece ser que ele deve ser usado em caches. O Dr. Jon Harrop apresentou um argumento muito bom contra o uso de WeakReferences em caches na resposta a esta pergunta.
Também ouvi muitas vezes desenvolvedores do AS3 falarem sobre o uso de referências fracas para economizar espaço na memória, mas com base nas conversas que tive, parece acrescentar complexidade sem necessariamente atingir o objetivo pretendido, e o comportamento do tempo de execução é imprevisível. Tanto é assim que muitos simplesmente desistem e, em vez disso, gerenciam o uso da memória com mais cuidado / otimizam seu código para consumir menos memória (ou fazer o trade-off de mais ciclos de CPU e menor espaço de memória).
O Dr. Jon Harrop também apontou em sua resposta que as referências fracas do .Net não são flexíveis, e há uma coleção agressiva de referências fracas no gen0. De acordo com o MSDN , referências longas e fracas oferecem a possibilidade de recriar um objeto but the state of the object remains unpredictable.
!
Dadas essas características, não consigo pensar em uma situação em que referências fracas sejam úteis, talvez alguém possa me esclarecer?
fonte
Respostas:
Encontrei aplicativos práticos legítimos de referências fracas nos três cenários a seguir que realmente aconteceram comigo pessoalmente:
Aplicativo 1: Manipuladores de eventos
Você é um empreendedor. Sua empresa vende um controle de linhas de ignição para o WPF. As vendas são excelentes, mas os custos de suporte estão prejudicando você. Muitos clientes reclamam de problemas de CPU e vazamentos de memória quando percorrem telas cheias de linhas de centelha. O problema é que o aplicativo está criando novas linhas de ignição à medida que são exibidas, mas a ligação de dados está impedindo que as antigas sejam coletadas de lixo. O que você faz?
Introduzir uma referência fraca entre a ligação de dados e seu controle, para que somente a ligação de dados não impeça mais a coleta de lixo do seu controle. Em seguida, adicione um finalizador ao seu controle que derruba a ligação de dados quando ela é coletada.
Aplicativo 2: Gráficos mutáveis
Você é o próximo John Carmack. Você inventou uma nova e engenhosa representação gráfica de superfícies de subdivisão hierárquica que faz com que os jogos de Tim Sweeney pareçam um Nintendo Wii. Obviamente, não vou lhe dizer exatamente como ele funciona, mas tudo se concentra nesse gráfico mutável, onde os vizinhos de um vértice podem ser encontrados em a
Dictionary<Vertex, SortedSet<Vertex>>
. A topologia do gráfico continua mudando à medida que o player corre. Há apenas um problema: sua estrutura de dados está descartando subgráficos inacessíveis à medida que é executada e você precisa removê-los ou perderá memória. Felizmente, você é um gênio e sabe que existe uma classe de algoritmos projetados especificamente para localizar e coletar subgráficos inacessíveis: coletores de lixo! Você leu a excelente monografia de Richard Jones sobre o assuntomas deixa você perplexo e preocupado com seu prazo iminente. O que você faz?Simplesmente, substituindo o seu
Dictionary
por uma tabela de hash fraca, você pode pegar o GC existente e coletar automaticamente seus subgráficos inacessíveis para você! Voltar a folhear anúncios da Ferrari.Aplicação 3: Decoração de árvores
Você está pendurado no teto de uma sala cíclica em um teclado. Você tem 60 segundos para examinar alguns GRANDES DADOS antes que alguém o encontre. Você veio preparado com um belo analisador baseado em fluxo que depende do GC para coletar fragmentos de AST depois de analisados. Mas você percebe que precisa de metadados extras em cada AST
Node
e precisa rapidamente. O que você faz?Você pode usar a
Dictionary<Node, Metadata>
para associar metadados a cada nó, mas, a menos que você os limpe, as referências fortes do dicionário para os nós AST antigos os manterão vivos e vazarão memória. A solução é uma tabela de hash fraca que mantém apenas referências fracas a chaves e o lixo coleta associações de valor-chave quando a chave se torna inacessível. Em seguida, à medida que os nós AST se tornam inacessíveis, eles são coletados como lixo e sua ligação de valor-chave é removida do dicionário, deixando os metadados correspondentes inacessíveis, para que também sejam coletados. Então, tudo o que você precisa fazer depois que o loop principal terminar é deslizar de volta pelo respiradouro, lembrando-se de substituí-lo assim que o guarda de segurança entrar.Observe que nas três aplicações do mundo real que realmente aconteceram comigo, eu queria que o GC coletasse o mais agressivamente possível. É por isso que esses são aplicativos legítimos. Todo mundo está errado.
fonte
ConditionalWeakTable
em .NET).ConditionalWeakTable
é isso que os aplicativos 2 e 3 usariam, enquanto algumas pessoas em outros posts realmente usamDictionary<WeakReference, T>
. Não sei por quê - você sempre terminaria com uma tonelada de nulosWeakReference
com valores que não podem ser acessados por nenhuma chave, independentemente de como você o faz. Ridik.Documento da Microsoft Weak Event Patterns .
fonte
Deixe-me colocar isso em primeiro lugar e voltar a ele:
Então, vamos começar do começo:
- desculpe-me antecipadamente por qualquer ofensa não intencional, mas voltarei ao nível "Dick and Jane" por um momento, já que nunca se pode contar para o público.
Então, quando você tem um objeto
X
- vamos especificá-lo como uma instânciaclass Foo
dele - ele NÃO PODE viver sozinho (principalmente verdadeiro); Da mesma maneira que "Nenhum homem é uma ilha", existem apenas algumas maneiras pelas quais um objeto pode ser promovido para a Ilha da Ilha - embora seja chamado de raiz da GC no CLR. Ser uma raiz do GC ou ter uma cadeia estabelecida de conexões / referências a uma raiz do GC é basicamente o que determina se oFoo x = new Foo()
lixo é coletado ou não .Se você não conseguir voltar para alguma raiz do GC por meio de heap ou stack stack, ficará efetivamente órfão e provavelmente será marcado / coletado no próximo ciclo.
Neste ponto, vejamos alguns exemplos horríveis:
Primeiro, nosso
Foo
:Bastante simples - não é seguro para threads, por isso não tente fazer isso, mas mantém uma "contagem de referência" aproximada de instâncias ativas e diminui quando elas são finalizadas.
Agora vamos dar uma olhada em
FooConsumer
:Portanto, temos um objeto que já é uma raiz própria do GC (bem ... para ser específico, ele será enraizado através de uma cadeia diretamente no domínio do aplicativo que está executando esse aplicativo, mas esse é outro tópico) que possui dois métodos de travar em uma
Foo
instância - vamos testá-lo:Agora, pelo exposto, você esperaria que o objeto que antes era referido
f
fosse "colecionável"?Não, porque agora existe outro objeto com uma referência a ele - o
Dictionary
nessaSingleton
instância estática.Ok, vamos tentar a abordagem fraca:
Agora, quando analisamos nossa referência ao
Foo
que era uma vezf
, não há mais referências "difíceis" ao objeto, portanto ele é colecionável - oWeakReference
criado pelo ouvinte fraco não impede isso.Bons casos de uso:
Manipuladores de eventos (embora leia isto primeiro: Eventos Fracos em C # )
Você tem uma situação em que poderia causar uma "referência recursiva" (ou seja, o objeto A se refere ao objeto B, que se refere ao objeto A, também conhecido como "vazamento de memória")(edit: derp, é claro que isso não é é verdade)Você deseja "transmitir" algo para uma coleção de objetos, mas não quer ser o que os mantém vivos; um
List<WeakReference>
pode ser mantido facilmente e até podado removendo onderef.Target == null
fonte
Como vazamentos lógicos que são realmente difíceis de rastrear, enquanto os usuários tendem a notar que a execução do software por um longo tempo tende a ocupar cada vez mais memória e a ficar cada vez mais lenta até serem reiniciados? Eu não.
Considere o que acontece se, quando o usuário solicitar a remoção do recurso do aplicativo acima,
Thing2
falhar ao lidar adequadamente com esse evento em:... e sob o qual um desses erros provavelmente seria detectado durante os testes e qual não seria e voaria sob o radar como um bug de caça furtivo. Propriedade compartilhada é, com mais frequência do que a maioria, uma ideia sem sentido.
fonte
Um exemplo muito ilustrativo de referências fracas usadas com bom efeito é o ConditionalWeakTable , usado pelo DLR (entre outros locais) para anexar "membros" adicionais aos objetos.
Você não quer que a mesa mantenha o objeto vivo. Este conceito simplesmente não poderia funcionar sem referências fracas.
Mas parece-me que todos os usos para referências fracas vieram muito tempo depois de serem adicionados à linguagem, pois as referências fracas fazem parte do .NET desde a versão 1.1. Parece apenas algo que você gostaria de adicionar, de modo que a falta de destruição determinística não o levará a um canto no que diz respeito aos recursos da linguagem.
fonte
WeakReference
tipo, pois a situação é muito mais complexa. Ele usa diferentes funcionalidades expostas pelo CLR.Se você possui uma camada de cache implementada com C #, é muito melhor colocar seus dados no cache como referências fracas, isso pode ajudar a melhorar o desempenho da camada de cache.
Pense que essa abordagem também pode ser aplicada à implementação da sessão. Como a sessão é um objeto de vida longa na maioria das vezes, pode ser que você não tenha memória para um novo usuário. Nesse caso, será muito melhor excluir outro objeto de sessão do usuário do que lançar OutOfMemoryException.
Além disso, se você tiver um objeto grande no seu aplicativo (alguma tabela grande de pesquisa, etc.), isso deve ser usado raramente e a recriação de um objeto desse tipo não é um procedimento muito caro. Então, é melhor ter como referência uma semana uma maneira de liberar sua memória quando você realmente precisar disso.
fonte