在调试和发行版中,WeakReference的行为有所不同(未附加调试器)。即使使用工厂方法 [英] WeakReference behaving differently in Debug and Release (no debugger attached). Even when using a factory method

查看:79
本文介绍了在调试和发行版中,WeakReference的行为有所不同(未附加调试器)。即使使用工厂方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[Do!我是个白痴..我在代码中将对象植根于其中。.]



我的代码可以在Release中按预期工作,但在Debug中失败。 / p>

我有一个包含 WeakReference 实例到其他对象的字典。在Release中,一旦不引用字典并进行收集,字典将按预期丢失其值。但是,在Debug中似乎没有发生……



即使在调试中,我也看到了其他 WeakReference 在Debug中被收集,但是字典中没有被收集。



下面的代码显示了这一点。即使我在它们之间添加了多个收集和延迟( Task.Delay(100)),它仍然不会消失。



任何想法如何强制WR无效?我不太介意,但是我有一个测试对此进行测试,它将在Debug中失败。



这里是代码:

 使用系统; 
使用System.Collections.Generic;
使用System.Linq;
使用System.Text;
使用System.Threading.Tasks;

命名空间ConsoleApplication5
{
class Program
{
static void Main(string [] args)
{
DoIt( );
Console.ReadLine();
}

私有静态异步void DoIt()
{
string key = k1;
var dict = new WeakItemDictionary< string,string>();
var s = dict.GetOrAdd(key,k => String.Concat( sdsdsd, sdsdsdsdsd));;
RunFullGCCollection();找到
var = dict.GetItemOrDefault(key);
Console.WriteLine(found == null?已收集对象:对象仍然存在);
}

private static void RunFullGCCollection()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}

///< summary>
///创建一个引用较弱的对象的字典,该字典将在不再使用时消失。
///向类添加函数时要小心-您需要考虑很多方案。
///请参阅GetOrAdd()如何工作以获取更多信息。
///< / summary>
///< typeparam name = K>字典键< / typeparam>
///< typeparam name = V>字典的值类型< / typeparam>
公共类WeakItemDictionary< K,V>其中V:class
{
public const int CleanPassFrequency = 10;
private Dictionary< K,WeakReference< V>> _dictionary =新字典< K,WeakReference< V>>();
private int _addCount = 0;

public V GetOrAdd(K key,Func< K,V> factory)
{
WeakReference< V>弱引用;
V值=空;
if(!_dictionary.TryGetValue(key,out weakRef))
{
value = factory(key);
weakRef = new WeakReference< V>(值);
_dictionary [key] =弱引用;
_addCount ++;
}

//如果该值为null,请尝试从弱引用中获取该值(以使其为根)。
if(value == null)
{
value = weakRef.GetTargetOrDefault();

//如果该值仍为null,则表示弱引用已清除。如果(value == null)
{
value = factory(key);我们需要重新创建(重新植根)

weakRef.SetTarget(value);
_addCount ++;
}
}

CleanIfNeeded();

返回值;
}

public V GetItemOrDefault(K key)
{
WeakReference< V>弱引用;
V值=空;
if(_dictionary.TryGetValue(key,out weakRef))
{
value = weakRef.GetTargetOrDefault();
}

返回值;
}

private void CleanIfNeeded()
{
Lazy< List< List> keysToRemove = new Lazy< List< K>>(false);

foreach(_dictionary中的变量项)
{
if(item.Value.IsDead())
{
keysToRemove.Value.Add(item 。键);
}
}

if(keysToRemove.IsValueCreated)
{
foreach(keysToRemove.Value中的变量项)
{
_dictionary.Remove(item);
}
}
}
}

公共静态类扩展名
{
public static bool IsDead< T>(this WeakReference< T>弱),其中T:类
{
T t;
bool结果=!weak.TryGetTarget(out t);
的返回结果;
}

公共静态T GetTargetOrDefault< T>(此WeakReference< T>弱),其中T:类
{
T t;
bool结果=!weak.TryGetTarget(out t);
返回t;
}


}
}


解决方案

  var s = dict.GetOrAdd(key,k => String.Concat( sdsdsd, sdsdsdsdsd)); 

您的 s 变量引用了目的。请注意,即使DoIt()方法尚未完成执行并且 s 变量仍存储在该方法的激活框架中,您仍会强制执行收集。当您运行不带调试器的Release版本时,此方法有效,这使垃圾收集器高效。但是当您调试时却没有。否则,发布配置首先存在的核心原因之一。



这种行为差异的技术原因在这篇文章



您不必担心,您只需要了解原因它的行为有所不同。您可以通过在调用GC.Collect()之前将 s 设置回null来更改结果。或将dict.GetOrAdd()调用移到另一个方法中。


[Doh! I am an idiot.. I am rooting the object right there in the code..]

I have code that works as expected in Release, but fails in Debug.

I have a Dictionary that contains WeakReference instances to other objects. In Release, the dictionary "loses" its values as expected, once they are not referenced and collection occurs. However, in Debug, it doesn’t seem to happen…

Even in debug, I do see other WeakReference getting collected in Debug, but the ones in the dictionary are not…

The code below shows this. Even when I add multiple Collects and delays between them (Task.Delay(100)), it still does not go away.

Any idea how to force the WRs to get nulled? I don’t mind too much, but I have a test that tests for this and it will fail in Debug.

Here’s the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            DoIt();
            Console.ReadLine();
        }

        private static async void DoIt()
        {
            string key = "k1";
            var dict = new WeakItemDictionary<string, string>();
            var s = dict.GetOrAdd(key, k => String.Concat("sdsdsd", "sdsdsdsdsd"));
            RunFullGCCollection();
            var found = dict.GetItemOrDefault(key);
            Console.WriteLine(found == null ? "Object got collected" : "Object is still alive");
        }

        private static void RunFullGCCollection()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
        }
    }

    /// <summary>
    /// Creates a dictionary of weakly referenced object that will disapear when no longer in use.
    /// Be careful when adding functions to the class - you need to take a bunch of scenarios into account.
    /// See how GetOrAdd() works for more info.
    /// </summary>
    /// <typeparam name="K">Key of the dictionary</typeparam>
    /// <typeparam name="V">Value type for the dictionary</typeparam>
    public class WeakItemDictionary<K, V> where V : class
    {
        public const int CleanPassFrequency = 10;
        private Dictionary<K, WeakReference<V>> _dictionary = new Dictionary<K, WeakReference<V>>();
        private int _addCount = 0;

        public V GetOrAdd(K key, Func<K, V> factory)
        {
            WeakReference<V> weakRef;
            V value = null;
            if (!_dictionary.TryGetValue(key, out weakRef))
            {
                value = factory(key);
                weakRef = new WeakReference<V>(value);
                _dictionary[key] = weakRef;
                _addCount++;
            }

            // If the value is null, try to get it from the weak ref (to root it).
            if (value == null)
            {
                value = weakRef.GetTargetOrDefault();

                // If the value is still null, means the weak ref got cleaned. We need to recreate (again, rooted)
                if (value == null)
                {
                    value = factory(key);
                    weakRef.SetTarget(value);
                    _addCount++;
                }
            }

            CleanIfNeeded();

            return value;
        }

        public V GetItemOrDefault(K key)
        {
            WeakReference<V> weakRef;
            V value = null;
            if (_dictionary.TryGetValue(key, out weakRef))
            {
                value = weakRef.GetTargetOrDefault();
            }

            return value;
        }

        private void CleanIfNeeded()
        {
            Lazy<List<K>> keysToRemove = new Lazy<List<K>>(false);

            foreach (var item in _dictionary)
            {
                if (item.Value.IsDead())
                {
                    keysToRemove.Value.Add(item.Key);
                }
            }

            if (keysToRemove.IsValueCreated)
            {
                foreach (var item in keysToRemove.Value)
                {
                    _dictionary.Remove(item);
                }
            }
        }
    }

    public static class Extensions
    {
        public static bool IsDead<T>(this WeakReference<T> weak) where T : class
        {
            T t;
            bool result = !weak.TryGetTarget(out t);
            return result;
        }

        public static T GetTargetOrDefault<T>(this WeakReference<T> weak) where T : class
        {
            T t;
            bool result = !weak.TryGetTarget(out t);
            return t;
        }


    }
}

解决方案

        var s = dict.GetOrAdd(key, k => String.Concat("sdsdsd", "sdsdsdsdsd"));

Your s variable has a reference to the object. Note that your force a collection even though the DoIt() method has not finished executing yet and the s variable is still stored in the activation frame of the method. That works when you run the Release build without a debugger attached, it makes the garbage collector efficient. But not when you debug. Otherwise one of the a core reasons why the Release configuration exists in the first place.

The technical reason for this difference in behavior is explained in detail in this post.

Not something you should fret about, you only need to understand why it behaves differently. You can alter the outcome by setting s back to null before calling GC.Collect(). Or moving the dict.GetOrAdd() call into another method.

这篇关于在调试和发行版中,WeakReference的行为有所不同(未附加调试器)。即使使用工厂方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆