【问题标题】:What is the priority of deleting(collect) objects in WeakReference by garbage collector?垃圾收集器在 WeakReference 中删除(收集)对象的优先级是多少?
【发布时间】:2020-10-21 22:20:06
【问题描述】:

我是 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%

【问题讨论】:

    标签: c# .net garbage-collection clr


    【解决方案1】:

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

    我的猜测是,当调用GC.Collect(0) 时,某些对象已被提升为第 1/2 代。如果你用 GC.Collect(2) 替换它,我希望没有任何对象活着。

    WeakReference is not typically a great idea for caching。我避免它,并在我想缓存某些东西时使用强引用。通常结合某种方式来估计内存使用量,以便能够设置内存上限。现在内存通常很充足,因此减少内存使用的重要性有限。

    还有memory pooling的相关概念,可以用来减少gen 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 个对象处于活动状态,在 GC 之后提供 0 个对象处于活动状态。请注意使用TryStartNoGCRegion 来防止GC 在创建对象时运行。如果我更改程序以确保将某些对象提升到第 1/2 代并且只收集第 0 代,我会得到一些幸存的对象。

    所以我想说我的观点仍然成立。 GC 将收集它收集的一代中的所有对象。除非你有一些特定的用例,否则你最好不要弄乱 WeakReferences。

    【讨论】:

    • (首先感谢您的回复)不,当我设置 GC.Collect(2) 时它仍然有对象活着,但我注意到了,如果我删除 GC.Collect() ,所有对象都会活着但是当缓存大小
    • @Saki7ty GC 将在运行时需要时运行。因此,如果您分配更多内存,预计它将运行。
    • 非常感谢,但是我有一个问题,当运行时调用 GC 时内存使用率很高,为什么它将某些对象提升到 gen 1/2 并且不删除所有对象,另一方面,当我调用 Gc.Collect() 它删除所有对象并且不将任何对象提升到下一代,运行时垃圾收集和 Gc.Collect() 之间有区别吗??
    • @salo7ty GC.Collect 或运行时收集应该没有区别。我最好的猜测是在运行 GC 时有一些东西会保留参考。例如,GC 可以在 Data-constructor 运行时运行,此时数据对象将处于活动状态,因此将被提升。
    猜你喜欢
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多