Weak References

Cu totii stim, ca limbajele .NET, printre care C# si VB, sunt bazate pe garbage collector, un mecanism de colectare si eliberare automata a obiectelor alocate in memorie. Asta inseamna ca dezvoltatorul aplicatiei nu mai este responsabil cu dealocarea obiectelor alocate. Acest lucru poate fi realizat doar daca la un moment dat, in timpul executiei programului, nu mai exista referinte la obiectul tinta. In cazul in care mai exista referinte la respectivul obiect, se spune ca acestea sunt referinte tari sau strong references.
In .NET Framework exista si posibilitatea colectarii si eliberarii automate de catre garbage collector a unui obiect din memorie, chiar daca mai exista referinte la el, daca acel obiect constituie o referinta slaba sau weak reference. Un astfel de obiect este inmagazinat intr-un obiect de tipul WeakReference, o clasa a carei obiecte inmagazinate pot fi colectate automat, chiar daca mai exista referinte spre ele. In momentul in care tinta unui obiect de tip WeakReference este copiata intr-un obiect ordinar, aceasta devine o strong reference si nu va mai fi colectata de garbage colector atat timp cat mai contine referinte spre ea.

Referintele slabe sunt utile in momentul in care folosim obiecte care ocupa mult loc in memorie si care pot fi reconstruite usor in momentul accesarii lor. Clasa WeakReference este foarte folosita la implementarea mecanismelor de caching.

Sa ilustram acum totul printr-un exemplu, copiat din MSDN. Exemplul prezinta o colectie de obiecte, care constituie cache-ul unei aplicatii. Acea colectie este realizata cu ajutorul unui dictionar de tip IDictionary<TKey, TValue> de obiecte de tip WeakReference in care tinta obiectelor WeakReference o constituie obiecte de tip Data (clasa definita de noi), care incapsuleaza un sir de octeti (bytes array). Sa vedem exemplul.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        // Create the cache
        var c = new Cache(50);

        // Randomly access objects in the cache
        var r = new Random();
        var dataName = string.Empty;
        for (var i = 0; i < c.Count; ++i)
            dataName = c[r.Next(c.Count)].Name;

        // Show results
        double regenPercent = c.RegenerationCount * 100 / c.Count;
        Console.WriteLine("Cache size: {0}, Regenerated: {1}%", c.Count, regenPercent);
    }
}

public class Cache
{
    // Dictionary to contain the cache
    private static Dictionary<int, WeakReference> _cache;

    // Track the number of times an object is regenerated
    private int _regenCount = 0;

    public Cache(int count)
    {
        _cache = new Dictionary<int, WeakReference>();

        // Add data objects with a short weak reference to the cache
        for (var i = 0; i < count; ++i)
            _cache.Add(i, new WeakReference(new Data(i), false));
    }

    // Returns the number of items in the cache
    public int Count { get { return _cache.Count; } }

    // Returns the number of times an object had to be regenerated
    public int RegenerationCount { get { return _regenCount; } }

    // Accesses a data object from the cache. If the object was reclaimed for
    // garbage collection, create a new data object at that index location.
    public Data this[int index]
    {
       get
        {
            // Obtain an instance of a data object from the cache of weak reference objects
            var d = _cache[index].Target as Data;
            if (d == null)
            {
                // Object was reclaimed, so generate a new one
                Console.WriteLine("Regenerate object at {0}: Yes", index);
                d = new Data(index);
                _regenCount++;
            }
            else
            {
                // Object was obtained with the weak reference
                Console.WriteLine("Regenerate object at {0}: No", index);
            }

            return d;
        }
    }
}

// This class creates byte arrays to simulate data
public class Data
{
    private byte[] _data;
    private string _name;

    public Data(int size)
    {
        _data = new byte[size * 1024];
        _name = size.ToString();
    }

    public string Name { get { return _name; } }
}

Rezultatul rularii exemplului arata ceva in genul urmator:

// Example of the last lines of the output:
//
// ...
// Regenerate object at 36: Yes
// Regenerate object at 8: Yes
// Regenerate object at 21: Yes
// Regenerate object at 4: Yes
// Regenerate object at 38: No
// Regenerate object at 7: Yes
// Regenerate object at 2: Yes
// Regenerate object at 43: Yes
// Regenerate object at 38: No
// Cache size: 50, Regenerated: 94%

Niciun comentariu:

Trimiteți un comentariu