更新cli / c ++数组<object ^>中的值会导致大量垃圾收集

时间:2016-02-29 14:12:43

标签: arrays garbage-collection c++-cli

以下方法用于将本机数据传输到托管数据 结构。 AppendDataToChannel方法确实分配了创建的数组 更大的结构。

我现在看到的是这个方法的执行时间是50%垃圾 采集。由于时间对我来说非常关键,我必须摆脱它。

我的假设是该数组中的“ - &gt; SetValue”导致了 被重写为“垃圾收集”的值。有快速的方法吗? 更新数组中的值而不会导致此类行为? (有数千次调用 - > gt; SetValue)

Trace of execution (method is called multiple times) 提前致谢, 尼古拉斯

template <typename Tnative, typename Tmanaged>
ChannelGroupData    ^   NativeToManaged(I_ChannelGroup &aChannelGroup, unsigned int index, unsigned int count)
{
    std::map< std::string, std::vector<Tnative> > v;
    aChannelGroup.GetData(v, index, count);

    ChannelGroupData^ result = gcnew ChannelGroupData(v.size());

    for (std::map< std::string, std::vector<Tnative> >::const_iterator cMIt = v.begin(); cMIt != v.end(); ++cMIt)
    {   
        std::string channelObjectPath(cMIt->first);
        I_Channel *aChannel = aChannelGroup.GetChannelByObjectPath(channelObjectPath);
        if (aChannel != NULL)
        {
            unsigned int vSize (cMIt->second.size());
            DataType aType = static_cast<DataType>(aChannel->GetDataType());
            int iIndex (0);
            const std::vector<Tnative> *pChannelDataV (&cMIt->second);
            array<Tmanaged>^ vResult = gcnew array<Tmanaged>(pChannelDataV->size());
            for (std::vector<Tnative>::const_iterator cDIt = pChannelDataV->begin(); cDIt != pChannelDataV->end(); ++cDIt, ++iIndex)
            {
                vResult->SetValue((Tmanaged)(gcnew Tnative(*cDIt)), iIndex);
            }
            result->AppendDataToChannel(gcnew System::String(channelObjectPath.c_str()), vResult,static_cast<int>( aType), iIndex);
            delete aChannel;
        }
    }
    return result;
}

1 个答案:

答案 0 :(得分:1)

  

我的假设是该数组中的“ - &gt; SetValue”导致被覆盖的值被“垃圾收集”。

这里没有问题:你在新数组上调用SetValue,因此每个元素都是默认值。如果Tmanaged是引用类型,则默认值为null,并且无需收集任何内容。如果Tmanaged是值类型,则根本不会收集垃圾。

副手,我看到你的代码存在两个问题:

for (std::vector<Tnative>::const_iterator cDIt ...)
{
    vResult->SetValue((Tmanaged)(gcnew Tnative(*cDIt)), iIndex);
}
  • 您正在gcnew上致电Tnative。如果是原生的,new会不合适?
  • 你已经有了Tnative个对象;它在你正在迭代的向量中。你为什么要建造另一个?

那就是说,我认为你的代码没有问题;我认为GC运作正常。

让我们考虑GC的工作原理:它有大量的对象,它存储在三个桶中。新分配的对象进入“Gen0”桶,然后它们移动到“Gen1”和“Gen2”桶,因为它们在垃圾收集中存活。

当分配新对象时,它们被放置在Gen 0中。当Gen 0填满时,GC会执行Gen 0的集合。任何不再使用的对象都会被收集,任何仍在使用的对象移动到第1代。(当第1代填满时,过程就会发生。我忘记第2代收集的触发条件是什么。)

所以,在你的方法中,你要分配一堆新对象。 (gcnew ChannelGroupData(v.size())gcnew array<Tmanaged>(pChannelDataV->size())gcnew Tnative(*cDIt)gcnew System::String。)根据您拥有的数据量,您可能完全有可能分配足够的新对象,即Gen 0填充,GC完成Gen 0集合。

我在MSDN上发现了a blog post,它谈到了GC代的大小。根据这一点,代数的大小可能会根据您使用的运行时间而有所不同,但Gen 0的64 MB是写入帖子时的典型值。

当您从磁盘读取数据时,Gen 0存储桶会填满,垃圾收集器将启动并执行Gen 0收集。 这是分配大量新内存时的正常行为。另外,如果您从磁盘读取的数据大于64 MB,那么所有对象都将存活到第1代,那就是' ll触发Gen 1系列!他们将在第二代中存活下来,你也可能最终获得Gen 2系列,而且这些系列比第0代和第一代更昂贵。

如果您真的不想在读取数据时发生GC,那么我会采取预分配策略:提前分配您需要的所有管理对象,让GC做了它的事情,因为你分配了所有新的内存,然后保持对象很长一段时间。从磁盘读取数据时,替换已分配的托管对象上的值,GC将没有任何理由可以运行。