Obsługa ESRI twierdzi, że odtworzyła problem i otworzyła raport o błędzie (NIM070156).
Stwierdziłem, że występuje przeciek pamięci (w niezarządzanej pamięci sterty), który występuje, gdy narzędzie w moim dodatku .NET / C # ArcMap wykonuje zapytanie przestrzenne (zwracając ICursor
z IFeatureClass.Search
z ISpatialFilter
filtrem zapytań). Wszystkie obiekty COM są zwalniane, gdy tylko nie są już potrzebne (za pomocą Marshal.FinalReleaseCOMObject
).
Aby to ustalić, najpierw skonfigurowałem sesję PerfMon z licznikami dla prywatnych bajtów, wirtualnych bajtów i zestawu roboczego ArcMap.exe i zauważyłem, że wszystkie trzy stale rosną (o około 500 KB na iterację) przy każdym użyciu narzędzia wykonującego zapytanie . Co najważniejsze, dzieje się tak tylko wtedy, gdy jest przeprowadzane w odniesieniu do klas elementów w SDE przy użyciu bezpośredniego połączenia (pamięć ST_Geometry, klient i serwer Oracle 11g) Liczniki pozostały stałe podczas korzystania z geobazy danych pliku, a także podczas łączenia się ze starszą instancją SDE korzystającą z połączenia aplikacji.
Następnie użyłem LeakDiag i LDGrapher (z pewnymi wskazówkami z tego postu na blogu ) i trzykrotnie zalogowałem Windows Heap Allocator: kiedy po raz pierwszy ładuję ArcMap i wybieram narzędzie do jego inicjalizacji, po kilkadziesiątowym uruchomieniu narzędzia i po uruchomieniu to jeszcze kilkadziesiąt razy.
Oto wyniki pokazane w domyślnym widoku LDGrapher (całkowity rozmiar):
Oto stos wywołań dla czerwonej linii:
Jak widać, SgsShapeFindRelation2
funkcja w sg.dll wydaje się być odpowiedzialna za wyciek pamięci.
Jak rozumiem, sg.dll to podstawowa biblioteka geometrii używana przez ArcObjects i SgsShapeFindRelation2
prawdopodobnie jest tam, gdzie stosowany jest filtr przestrzenny.
Zanim zrobię cokolwiek innego, chciałem tylko sprawdzić, czy ktokolwiek napotkał ten problem (lub coś podobnego) i co, jeśli mógł coś z tym zrobić. Co może być przyczyną takiego stanu rzeczy tylko w przypadku bezpośredniego połączenia? Czy to brzmi jak błąd w ArcObjects, problem z konfiguracją lub problem z programowaniem?
Oto minimalna działająca wersja metody, która powoduje takie zachowanie:
private string GetValueAtPoint(IPoint pPoint, IFeatureClass pFeatureClass, string pFieldName)
{
string results = "";
ISpatialFilter pSpatialFilter = null;
ICursor pCursor = null;
IRow pRow = null;
try
{
pSpatialFilter = new SpatialFilterClass();
pSpatialFilter.Geometry = pPoint;
pSpatialFilter.GeometryField = pFeatureClass.ShapeFieldName;
pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;
pCursor = (ICursor)pFeatureClass.Search(pSpatialFilter, false);
pRow = pCursor.NextRow();
if (pRow != null)
results = pRow.get_Value(pFeatureClass.FindField(pFieldName)).ToString();
}
finally
{
// Explicitly release COM objects
if (pRow != null)
Marshal.FinalReleaseComObject(pRow);
if (pCursor != null)
Marshal.FinalReleaseComObject(pCursor);
if (pSpatialFilter != null)
Marshal.FinalReleaseComObject(pSpatialFilter);
}
return results;
}
Oto mój kod obejścia oparty na poniższej dyskusji z Ragi:
private bool PointIntersectsFeature(IPoint pPoint, IFeature pFeature)
{
bool returnVal = false;
ITopologicalOperator pTopoOp = null;
IGeometry pGeom = null;
try
{
pTopoOp = ((IClone)pPoint).Clone() as ITopologicalOperator;
if (pTopoOp != null)
{
pGeom = pTopoOp.Intersect(pFeature.Shape, esriGeometryDimension.esriGeometry0Dimension);
if (pGeom != null && !(pGeom.IsEmpty))
returnVal = true;
}
}
finally
{
// Explicitly release COM objects
if (pGeom != null)
Marshal.FinalReleaseComObject(pGeom);
if (pTopoOp != null)
Marshal.FinalReleaseComObject(pTopoOp);
}
return returnVal;
}
private string GetValueAtPoint(IPoint pPoint, IFeatureClass pFeatureClass, string pFieldName)
{
string results = "";
ISpatialFilter pSpatialFilter = null;
IFeatureCursor pFeatureCursor = null;
IFeature pFeature = null;
try
{
pSpatialFilter = new SpatialFilterClass();
pSpatialFilter.Geometry = pPoint;
pSpatialFilter.GeometryField = pFeatureClass.ShapeFieldName;
pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects;
pFeatureCursor = pFeatureClass.Search(pSpatialFilter, true);
pFeature = pFeatureCursor.NextFeature();
while (pFeature != null)
{
if (PointIntersectsFeature(pPoint, pFeature))
{
results = pFeature.get_Value(pFeatureClass.FindField(pFieldName)).ToString();
break;
}
pFeature = pFeatureCursor.NextFeature();
}
}
finally
{
// Explicitly release COM objects
if (pFeature != null)
Marshal.FinalReleaseComObject(pFeature);
if (pFeatureCursor != null)
Marshal.FinalReleaseComObject(pFeatureCursor);
if (pSpatialFilter != null)
Marshal.FinalReleaseComObject(pSpatialFilter);
}
return results;
}
źródło
Odpowiedzi:
To wygląda na błąd.
SG zawiera biblioteki geometrii ArcSDE, a nie biblioteki geometrii ArcObjects ... jest on używany jako filtr wstępny, zanim test trafi do bibliotek geometrii ArcObjects.
Spróbuj tego:
Pomiń ten wiersz:
pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;
a ponieważ nie zapisujesz odwołania do wiersza, nie musisz używać kursorów recyklingu, więc przełącz fałszywą flagę na wartość true.
Powinieneś zauważyć poprawę zarówno zużycia pamięci, jak i szybkości działania. Niemniej jednak, jeśli błąd zostanie nadal trafiony, mam nadzieję, że dramatycznie go opóźni :)
źródło
Jeśli ktoś nadal jest tym zainteresowany, zostało to naprawione w wersji 10.1.
Numer pomocy technicznej ESRI: NIM070156 i NIM062420
http://support.esri.com/en/bugs/nimbus/TklNMDcwMTU2 http://support.esri.com/en/bugs/nimbus/TklNMDYyNDIw
źródło
Możesz wypróbować następujący wzór zamiast
try / finally { Marshal.FinalReleaseComObject(...) }
:Pracując również z Direct Connect, odniosłem pewien sukces w pętlach, wymuszając
System.GC.Collect()
okresowo (co tak wiele iteracji), jakkolwiek to wygląda paskudnie.źródło