单线程时不会发生多线程时指针数组的奇数内存泄漏
Odd memory leak with pointer arrays when multithreading that do not occur while single threading
经过一整天的调试,我发现调用以下函数时总是会发生内存泄漏:
void merge(TContainer<T> List2)
{
TContainer<T> temp(this->Size);
for (int i = 0; i < this->Size; i++)
{
temp.Interface[i] = this->Interface[i];
}
this->Interface = new T[Size + List2.size()];
Size = Size + List2.size();
for(int i = 0; i < List2.size(); i++)
{
Interface[i] = List2[i];
}
for(int i = List2.size(); i < Size; i++)
{
Interface[i] = temp[i];
};
delete[] temp.Interface;
}
内码:
TContainer_Short<unsigned short> Temp = TContainer_Short<unsigned short>(0);
for(int i = (ToUpdate.size() - 1); i >= 0; i--)
{
UpdateInUse = true;
ToUpdate[i].Ad.push_back(AdQueue[i].Indirect[0].Address);
auto Entity = ToUpdate[i];
UpdateInUse = false;
float HighestScore = 0;
int Index = 0;
//Go through all the advertisements on their queue
//Make sure our last index is always the next plot point in our story.
for(int j = 0; j < ToUpdate[i].Ad.size(); j++)
{
AdvertisementBase Ad = *World::get()->getTemplateAd(Entity.Ad[j]);
float temp = returnScore(Entity.Index, Ad);
//If its higher than our current score, set i to this index
if(temp > HighestScore)
Index = j;
}
//Index is last pos when we're currently continuing our plot queue. We haven't changed our mind about what advertisement we want
if(Index !=(Entity.Ad.size() - 1))
{
AdvertisementBase *Ad = World::get()->getTemplateAd(Entity.Ad[Index]);
this->reduceAdChain(Entity.Index, Ad);
}
else
{
//Makes sure that the entity is on track for the next goal that it had already determined
plan(Entity.Index,AdQueue.Interface[Entity.Index].Indirect[0].Address);
}
Temp.push_back(Entity.Index);
ToUpdate.pop_back();
}
if(!ExecutingInUse)
{
ExecutingInUse = true;
Executing.merge(Temp);
ExecutingInUse = false;
}
delete[] Temp.Interface;
}
但是,我似乎无法弄清楚为什么只有在有多个线程时才会出现这种情况。数组本身一次只被一个线程引用,(Atomic),所以它不应该是个问题。
删除 Executing::merge 引用使内存泄漏消失,并且在单线程场景中肯定会显着提高性能。
更奇怪的是merge用在了其他地方:
void reduceAdChain(unsigned short Index, TContainer<AdvertisementReference> Ads)
{
AdQueue[Index].Indirect.merge(Ads);
}
即使 reduceAdChain 被调用的频率几乎比 Executing::merge 高出整整一个数量级,也不会造成内存泄漏。并删除该区域的合并,不会产生明显的性能提升,即使
A) reduceAdChain 为合并采用的数组平均大小几乎是传入 Executing::merge
的数组的 3 倍
和
B) reduceAdChain 的总长度几乎是 Executing 长度的 5 倍。
但是,执行确实会在每次迭代结束时被清除。
这是我在多线程环境中 运行 经历过的最奇怪的事情之一。
使用执行的地方:
if(!m_simulated_entities[i]->ExecutingInUse)
{
for (int j = 0; j < m_simulated_entities[i]->Executing.size(); )
{
// Retrieve Tag Data and Update Constants
m_simulated_entities[i]->ExecutingInUse = true;
ExecutingIndex = m_simulated_entities[i]->Executing[j];
m_simulated_entities[i]->ExecutingInUse = false;
TagIndex = m_simulated_entities[i]->TagIndicesPerEntity[ExecutingIndex];
now = std::chrono::system_clock::now();
time_now = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
if (m_simulated_entities[i]->Timing[m_simulated_entities[i]->Executing[j]].TimeConstant == 0)
{
//Make sure all of our new attribute values still allow this entity to live
if(!m_simulated_entities[i]->updateTick(ExecutingIndex))
m_simulated_entities[i]->removeInstance(ExecutingIndex);
else
{
//Compute our new transfer constant
m_simulated_entities[i]->prepare(ExecutingIndex);
//Update the tagging system
m_simulated_entities[i]->updateTags(ExecutingIndex);
//Search for new decisions
m_simulated_entities[i]->ToSearch.push_back(ExecutingIndex);
}
//Remove the index from execution
m_simulated_entities[i]->ExecutingInUse = true;
m_simulated_entities[i]->Executing.Remove(j);
m_simulated_entities[i]->ExecutingInUse = false;
}
else if (time_now - m_simulated_entities[i]->Timing[ExecutingIndex].LastUpdateTime > updateConstants[TagIndex])
{
m_simulated_entities[i]->Timing[ExecutingIndex].TimeConstant--;
m_simulated_entities[i]->Timing[ExecutingIndex].LastUpdateTime = time_now;
j++;
}
}
}
对于测试,updateTick 被禁用并且将始终 return 为真,因为允许该函数正确执行会使查找内存泄漏变得更加困难。
在合并函数中:
this->Interface = new T[Size + List2.size()];
你应该检查指针this->Interface
是否是NULL
,如果不是,应该先释放它。否则多次调用merge函数会泄漏。
所以代码将是:
if (this->Interface != NULL)
delete[] this->Interface;
this->Interface = new T[Size + List2.size()];
抱歉提出可能很愚蠢的问题:如果您有 "ToUpdate[i].Ad" 等向量,为什么不也使用 "this->Interface" 作为向量?这可以为您节省大量查找此泄漏的时间。
经过一整天的调试,我发现调用以下函数时总是会发生内存泄漏:
void merge(TContainer<T> List2)
{
TContainer<T> temp(this->Size);
for (int i = 0; i < this->Size; i++)
{
temp.Interface[i] = this->Interface[i];
}
this->Interface = new T[Size + List2.size()];
Size = Size + List2.size();
for(int i = 0; i < List2.size(); i++)
{
Interface[i] = List2[i];
}
for(int i = List2.size(); i < Size; i++)
{
Interface[i] = temp[i];
};
delete[] temp.Interface;
}
内码:
TContainer_Short<unsigned short> Temp = TContainer_Short<unsigned short>(0);
for(int i = (ToUpdate.size() - 1); i >= 0; i--)
{
UpdateInUse = true;
ToUpdate[i].Ad.push_back(AdQueue[i].Indirect[0].Address);
auto Entity = ToUpdate[i];
UpdateInUse = false;
float HighestScore = 0;
int Index = 0;
//Go through all the advertisements on their queue
//Make sure our last index is always the next plot point in our story.
for(int j = 0; j < ToUpdate[i].Ad.size(); j++)
{
AdvertisementBase Ad = *World::get()->getTemplateAd(Entity.Ad[j]);
float temp = returnScore(Entity.Index, Ad);
//If its higher than our current score, set i to this index
if(temp > HighestScore)
Index = j;
}
//Index is last pos when we're currently continuing our plot queue. We haven't changed our mind about what advertisement we want
if(Index !=(Entity.Ad.size() - 1))
{
AdvertisementBase *Ad = World::get()->getTemplateAd(Entity.Ad[Index]);
this->reduceAdChain(Entity.Index, Ad);
}
else
{
//Makes sure that the entity is on track for the next goal that it had already determined
plan(Entity.Index,AdQueue.Interface[Entity.Index].Indirect[0].Address);
}
Temp.push_back(Entity.Index);
ToUpdate.pop_back();
}
if(!ExecutingInUse)
{
ExecutingInUse = true;
Executing.merge(Temp);
ExecutingInUse = false;
}
delete[] Temp.Interface;
}
但是,我似乎无法弄清楚为什么只有在有多个线程时才会出现这种情况。数组本身一次只被一个线程引用,(Atomic),所以它不应该是个问题。
删除 Executing::merge 引用使内存泄漏消失,并且在单线程场景中肯定会显着提高性能。
更奇怪的是merge用在了其他地方:
void reduceAdChain(unsigned short Index, TContainer<AdvertisementReference> Ads)
{
AdQueue[Index].Indirect.merge(Ads);
}
即使 reduceAdChain 被调用的频率几乎比 Executing::merge 高出整整一个数量级,也不会造成内存泄漏。并删除该区域的合并,不会产生明显的性能提升,即使
A) reduceAdChain 为合并采用的数组平均大小几乎是传入 Executing::merge
的数组的 3 倍和
B) reduceAdChain 的总长度几乎是 Executing 长度的 5 倍。
但是,执行确实会在每次迭代结束时被清除。
这是我在多线程环境中 运行 经历过的最奇怪的事情之一。
使用执行的地方:
if(!m_simulated_entities[i]->ExecutingInUse)
{
for (int j = 0; j < m_simulated_entities[i]->Executing.size(); )
{
// Retrieve Tag Data and Update Constants
m_simulated_entities[i]->ExecutingInUse = true;
ExecutingIndex = m_simulated_entities[i]->Executing[j];
m_simulated_entities[i]->ExecutingInUse = false;
TagIndex = m_simulated_entities[i]->TagIndicesPerEntity[ExecutingIndex];
now = std::chrono::system_clock::now();
time_now = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
if (m_simulated_entities[i]->Timing[m_simulated_entities[i]->Executing[j]].TimeConstant == 0)
{
//Make sure all of our new attribute values still allow this entity to live
if(!m_simulated_entities[i]->updateTick(ExecutingIndex))
m_simulated_entities[i]->removeInstance(ExecutingIndex);
else
{
//Compute our new transfer constant
m_simulated_entities[i]->prepare(ExecutingIndex);
//Update the tagging system
m_simulated_entities[i]->updateTags(ExecutingIndex);
//Search for new decisions
m_simulated_entities[i]->ToSearch.push_back(ExecutingIndex);
}
//Remove the index from execution
m_simulated_entities[i]->ExecutingInUse = true;
m_simulated_entities[i]->Executing.Remove(j);
m_simulated_entities[i]->ExecutingInUse = false;
}
else if (time_now - m_simulated_entities[i]->Timing[ExecutingIndex].LastUpdateTime > updateConstants[TagIndex])
{
m_simulated_entities[i]->Timing[ExecutingIndex].TimeConstant--;
m_simulated_entities[i]->Timing[ExecutingIndex].LastUpdateTime = time_now;
j++;
}
}
}
对于测试,updateTick 被禁用并且将始终 return 为真,因为允许该函数正确执行会使查找内存泄漏变得更加困难。
在合并函数中:
this->Interface = new T[Size + List2.size()];
你应该检查指针this->Interface
是否是NULL
,如果不是,应该先释放它。否则多次调用merge函数会泄漏。
所以代码将是:
if (this->Interface != NULL)
delete[] this->Interface;
this->Interface = new T[Size + List2.size()];
抱歉提出可能很愚蠢的问题:如果您有 "ToUpdate[i].Ad" 等向量,为什么不也使用 "this->Interface" 作为向量?这可以为您节省大量查找此泄漏的时间。