垃圾收集器在WeakReference中删除(收集)对象的优先级是什么?

时间:2020-07-01 12:54:45

标签: c# .net garbage-collection clr

我是C#的新手,我正在学习垃圾收集器,我的参考课薄弱

here在msdn中使用弱引用的示例

示例:

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        // Create the cache.
        int cacheSize = 50;
        Random r = new Random();
        Cache c = new Cache(cacheSize);

    string DataName = "";
    GC.Collect(0);

    // Randomly access objects in the cache.
    for (int i = 0; i < c.Count; i++) {
        int index = r.Next(c.Count);

        // Access the object by getting a property value.
        DataName = c[index].Name;
    }
    // Show results.
    double regenPercent = c.RegenerationCount/(double)c.Count;
    Console.WriteLine("Cache size: {0}, Regenerated: {1:P2}%", c.Count, 
    regenPercent);
    
}

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

// Track the number of times an object is regenerated.
int regenCount = 0;

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

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

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

// Number of times an object needs to be regenerated.
public int RegenerationCount
{
    get { return regenCount; }
}

// Retrieve a data object from the cache.
public Data this[int index]
{
    get {
        Data d = _cache[index].Target as Data;
        if (d == null) {
            // If the object was reclaimed, generate a new one.
            Console.WriteLine("Regenerate object at {0}: Yes", index);
            d = new Data(index);
            _cache[index].Target = d;
            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();
}

// Simple property.
public string Name
{
    get { return _name; }
}
}
 // 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%

通过垃圾收集器删除WeakReference中的对象的优先级是什么?

为什么GC选择了此对象来去除94%的物质并保留了6%

1 个答案:

答案 0 :(得分:1)

WeakReferences允许对引用对象进行垃圾回收。并且垃圾收集器将收集其正在收集的世代中的所有可收集对象。

我的猜测是,在发生对GC.Collect(0)的调用时,某些对象已提升为gen 1/2。如果您将其替换为GC.Collect(2),我希望没有任何对象存活。

WeakReference is not typically a great idea for caching。我避免使用它,并在要缓存某些内容时使用强引用。通常结合某种方式来估计内存使用情况以能够设置内存上限。如今,内存通常很丰富,因此减少内存使用的重要性有限。

还有memory pooling的相关概念,当使用大内存缓冲区时,可以用来减少第2代GC。

编辑:

简化了您的示例:

    static void Main(string[] args)
    {
        for (int i = 0; i < 100; i++)
        {
            GC.TryStartNoGCRegion(10000000);
            var cache = new Cache(50);
            Console.Write("Objects alive Before: " + cache.CountObjectsAlive());
            GC.EndNoGCRegion();
            GC.Collect(2, GCCollectionMode.Forced);
            Console.WriteLine("\tAfter : " + cache.CountObjectsAlive());
        }
        Console.ReadKey();
    }

public class Cache
{
    // Dictionary to contain the cache.
    Dictionary<int, WeakReference> _cache = new Dictionary<int, WeakReference>();

    public Cache(int count)
    {
        // Add objects with a short weak reference to the cache.
        for (int i = 0; i < count; i++)
        {
            _cache.Add(i, new WeakReference(new byte[i * 1024], false));
        }
    }
    public int CountObjectsAlive() => _cache.Values.Count(v => v.Target != null);
}

这始终为GC提供50个活动对象,之后为0个活动对象。请注意使用TryStartNoGCRegion来防止GC在创建对象时运行。如果我更改程序以确保将某些对象提升为第1/2代并且仅收集第0代,那么我会得到一些尚存的对象。

所以我要说的还是。 GC将收集其收集的世代中的所有对象。除非您有一些特定的用例,否则最好不要弄混WeakReferences。