We're running into memory leaks using the function
Esri::ArcGISRuntime::Part* PartCollection::part(int index) const of PartCollection Class | ArcGIS Maps SDK for Qt | Esri Developer
We use the most recent ArcGIS Maps SDK 200.5, on a Windows 10 machine.
We'd like to display a history trail of moving objects on the map. Whenever an object updates its position, the new position is added to the history trail. Old positions are removed from the history trail.
We do it this way:
A PolylineBuilder is assigned to each Object. The PolylineBuilder lives as long as the object lives. Whenever a point is too old we remove it
auto part = m_polyLineBuilder->parts()->part(0); // Memory leak in ->part(0)
for (int i = 0; i < nrToRemove; i++) {
part->removePoint(0);
}
m_graphic->setGeometry(m_polyLineBuilder->toGeometry());
The function to remove old points is called via a QTimer, and is thus called relatively often. Each time `auto part = m_polyLineBuilder->parts()->part(0);` is called we see in memory profiling that two objects are created that are never deleted.
Even better demonstration: Calling ->part(0) in a big for loop causes the memory consumption to rise dramatically:
for (int i = 0; i < 1000; i++) {
m_polyLineBuilder->parts()->part(0); // Memory leak
}
The parent of the returned part is the part collection, so there is also no transfer of ownership... Are we doing something wrong?
We created following snapshots of the memory, as you can see with increasing memory.
Here's the allocation diff showing that many new pointer objects were created.
Looking at the instances we see that almost all of the new allocations come from PartCollection::part(int index). Here's the first allocation:
And here's the second allocation:
Solved! Go to Solution.
@imbachb thanks for the detailed report. I can reproduce what you're seeing. We are incorrectly allocating a new part every time this is called for some internal reasons related to how we cache collections.
m_polyLineBuilder->parts()->part(0)
As soon as the parent goes away, it all goes away but that's not very helpful since parents are typically long-lived.
Here's some test code I whipped up, along with a temporary workaround for the meantime.
qDebug() << "Test started";
auto* m_polyLineBuilder = new PolylineBuilder(SpatialReference::webMercator(), this);
m_polyLineBuilder->addPoint(0, 0);
m_polyLineBuilder->addPoint(1, 1);
m_polyLineBuilder->addPoint(2, 2);
qDebug() << QString("PolylineBuilder part count: %1").arg(m_polyLineBuilder->parts()->size());
for (int i = 0; i < 10'000'000; i++) {
if (i % 1'000'000 == 0) {
qDebug() << QString("i: %1").arg(i);
}
auto* partCollection = m_polyLineBuilder->parts();
//auto* part = partCollection->part(0);
std::unique_ptr<Part> part {partCollection->part(0)}; // workaround
}
qDebug() << "Test finished";
Deleting the part shouldn't alter the contents. It will be recreated (as you found) on the next call.
I'll get an internal bug logged so we can see about addressing this in an upcoming release. Thank you for letting us know about this.
@imbachb thanks for the detailed report. I can reproduce what you're seeing. We are incorrectly allocating a new part every time this is called for some internal reasons related to how we cache collections.
m_polyLineBuilder->parts()->part(0)
As soon as the parent goes away, it all goes away but that's not very helpful since parents are typically long-lived.
Here's some test code I whipped up, along with a temporary workaround for the meantime.
qDebug() << "Test started";
auto* m_polyLineBuilder = new PolylineBuilder(SpatialReference::webMercator(), this);
m_polyLineBuilder->addPoint(0, 0);
m_polyLineBuilder->addPoint(1, 1);
m_polyLineBuilder->addPoint(2, 2);
qDebug() << QString("PolylineBuilder part count: %1").arg(m_polyLineBuilder->parts()->size());
for (int i = 0; i < 10'000'000; i++) {
if (i % 1'000'000 == 0) {
qDebug() << QString("i: %1").arg(i);
}
auto* partCollection = m_polyLineBuilder->parts();
//auto* part = partCollection->part(0);
std::unique_ptr<Part> part {partCollection->part(0)}; // workaround
}
qDebug() << "Test finished";
Deleting the part shouldn't alter the contents. It will be recreated (as you found) on the next call.
I'll get an internal bug logged so we can see about addressing this in an upcoming release. Thank you for letting us know about this.