Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在发布版本中的foreach迭代之间释放内存?_C#_Foreach_Garbage Collection - Fatal编程技术网

C# 在发布版本中的foreach迭代之间释放内存?

C# 在发布版本中的foreach迭代之间释放内存?,c#,foreach,garbage-collection,C#,Foreach,Garbage Collection,更新 显然,最初的问题令人困惑,所以我将尝试简化它 我正在使用一个复杂的算法,该算法提供一个对象列表(比如一家公司)。 对于这些公司中的每一家,我都必须加载大量数据(比如员工列表) 上市公司 { 公共字符串名称{get;set;}=”“; 公共列表EmployeeList{get;set;}=new List(); } 公营雇员 { 公共字符串FirstName{get;set;}=“Random first name”; 公共字符串LastName{get;set;}=“随机姓氏”; } 公共

更新 显然,最初的问题令人困惑,所以我将尝试简化它

我正在使用一个复杂的算法,该算法提供一个对象列表(比如一家公司)。
对于这些公司中的每一家,我都必须加载大量数据(比如员工列表)

上市公司
{
公共字符串名称{get;set;}=”“;
公共列表EmployeeList{get;set;}=new List();
}
公营雇员
{
公共字符串FirstName{get;set;}=“Random first name”;
公共字符串LastName{get;set;}=“随机姓氏”;
}
公共内存测试()
{
//对复杂算法进行了仿真。。。
//我不能改变我如何得到这份名单,我的问题不是关于这部分。
List companyList=新列表();
对于(int i=0;i<50000;i++)
{
添加(新公司(){Name=“Random Company Name”+i});
}
//模拟细节加载。这是内存被填满的地方
foreach(公司列表中的公司)
{
company.EmployeeList.AddRange(新员工[25000]);
//做一些计算并保存到数据库。。。
}
}
这段代码的问题在于每次迭代期间分配的内存直到循环结束才会释放

阅读后,我希望JIT能够确定迭代后不会使用公司引用,因为公司列表在foreach之外没有使用:

在发布版本中,JIT能够查看程序结构以确定最后一点 在执行过程中,一个变量可以被方法使用,并且在执行时会丢弃它 不再需要了

。。。但遗憾的是,JIT并没有推断那么远

为了使用尽可能少的内存,我的问题如下:是否有一种方法使循环通过集合并在每次迭代之间删除对元素的引用

如果您不想与公司/员工一起工作,这里有一个更通用的示例

Dictionary<int, List<string>> dict = new Dictionary<int, List<string>>();

for (int i = 0; i < 100000; i++)
{
    dict.Add(i, new List<string>());
}

foreach (var item in dict)
{
    item.Value.AddRange(new string[25000]);
}
Dictionary dict=new Dictionary();
对于(int i=0;i<100000;i++)
{
dict.Add(i,新列表());
}
foreach(dict中的var项目)
{
item.Value.AddRange(新字符串[25000]);
}

foreach
正在使用枚举器概念。借助于
.MoveNext()
方法,它借助于
.Current
属性一个接一个地观察集合中的项目。换言之,您的代码将被翻译为:

List.Enumerator Enumerator=new List().GetEnumerator();
尝试
{
while(枚举数.MoveNext())
{
Compagny current=枚举数.current;
负载触点(电流);
current.Log=“非常大的字符串…”;
}
}
最后
{
((IDisposable)枚举数).Dispose();
}
如您所见,每个
var compagny
最终都只是一个引用类型的局部变量。每次迭代中都会替换此引用。因此,从这个角度来看,
current
变量在迭代期间成为一些
Compagny
对象的附加根

这里的区别可能是这样一个根的范围。在调试构建的情况下,它实际上是底层
{}
while循环的整个
{}
块(实际上,从CIL的角度来看,是整个方法)。在发布构建的情况下,JIT更积极地报告根。因此,它将以不再需要的速度报告
current
。在您的例子中,您使用的是
current
,直到
{}
的末尾,所以它是相同的

如果在块结束之前不使用
compagny
,则可以看到差异,如:

foreach(compagnyList中的var compagny)
{
这是LoadContacts(compagny);
this.LoadNotes(compagny.ContactList)
compagny.Log=“非常大的字符串…”;
CallingHereLongLastingMethod();//调试-compagny仍然是根
//发布-compagny不再是根目录
}
但是。。。所有这些都无关紧要,因为
current
只是一个额外的对象根。对象本身(“compagny”)仍由
compagnyList
本身作为根


“GC是否足够聪明,能够理解公司对象在foreach迭代之后不会被重用,并将其标记为垃圾收集的候选对象(尽管compagnyList将保留在堆栈上)?”这个问题表明您在理解引用类型和GC基础知识方面存在一些问题<代码>列表是一种参考类型,它包含参考(作为局部变量存在于堆栈上)和公司的数据收集。

我将在这里修改我的原始答案。这并不是要打败众所周知的死马,但我只想强调,若您必须从集合中弹出项目以防止集合在内存中扎根对象,那个么几乎可以肯定的是,您的应用程序的设计存在一些问题。在某些情况下,这可能是一个很好的设计,但我经常说,事实并非如此

让我们以Smurf的公司场景为例,对其稍加修改,使其完全不保留集合中的大型对象

我将忽略公司对象字典。它从来没有被用作字典,只是作为收藏。还需要注意的是,字典中的内容并不是一个完整的公司对象。我们必须使用原始的company对象来检索额外的数据。我们通常称之为钥匙。因此,我们将有一个密钥流,而不是字典。以下是关键对象:

public class CompanyKey
{ }
以及用于生成密钥的数据源。这实际上可能是Smurfs字典,但出于我们的目的,我们将使其成为迭代器方法。这样,就没有什么东西能把这些东西留在记忆中了。如果
public class CompanyKey
{ }
public class CompanyKeySource
{
    public IEnumerable<CompanyKey> GetKeys()
    {
        for(int i =0;i < 10;++i)
            yield return new CompanyKey();
    }
}
public class Company
{
    public EmployeeData Employees { get; set; }
}
public class Employee
{
    public string[] LotOfData { get; set; }
}
public class CompanyDataRepository
{
    public IEnumerable<Company> GetCompanyDetails(IEnumerable<CompanyKey> keys)
    {
        foreach (var key in keys)
        {
            yield return new Company() { Employees = GetEmployees(key) };
        }
    }

    public EmployeeData GetEmployees(CompanyKey key) =>
        new EmployeeData() { LotOfData = new string[2500] };
}
    static void Main(string[] _)
    {
        CompanyDataRepository repository = new CompanyDataRepository();
        CompanyKeySource keySource = new CompanyKeySource();

        var keys = keySource.GetKeys();

        foreach (var company in repository.GetCompanyDetails(keys))
        {
            // do whatever it is you're doing with your companies...
        }
    }
public static IEnumerable<T> PopEnumerable<T>(this List<T> list)
{
    while (list.Count > 0)
    {
        yield return list[0];
        list.RemoveAt(0);
    }
}
foreach (Company company in companyList.PopEnumerable())
{
    company.EmployeeList.AddRange(new Employee[25000]);
}