Mengatasi kebocoran Memori di IFeatureClass.Search (hanya di SDE dengan koneksi langsung) dari ArcObjects?

16

Dukungan ESRI mengatakan mereka telah mereproduksi masalah ini dan telah membuka laporan bug (NIM070156).

Saya telah menentukan bahwa ada kebocoran memori (dalam memori tumpukan yang tidak dikelola) yang terjadi ketika alat di .NET / C # ArcMap add-in melakukan kueri spasial (mengembalikan ICursordari IFeatureClass.Searchdengan ISpatialFilterfilter kueri). Semua objek COM dilepaskan segera setelah tidak diperlukan lagi (menggunakan Marshal.FinalReleaseCOMObject).

Untuk menentukan ini, saya pertama kali mengatur sesi PerfMon dengan penghitung untuk ArcMap.exe Private Bytes, Virtual Bytes dan Working Set, dan mencatat bahwa ketiganya terus meningkat (sekitar 500KB per iterasi) dengan setiap penggunaan alat yang melakukan kueri . Yang terpenting, ini hanya terjadi ketika dilakukan terhadap kelas fitur pada SDE menggunakan koneksi langsung (penyimpanan ST_Geometry, klien dan server Oracle 11g). Penghitung tetap konstan saat menggunakan file geodatabase, serta saat menghubungkan ke instance SDE yang lebih lama yang menggunakan aplikasi connect.

Saya kemudian menggunakan LeakDiag dan LDGrapher (dengan beberapa panduan dari posting blog ini ) dan mencatat Windows Heap Allocator sebanyak tiga kali: ketika saya pertama kali memuat ArcMap dan memilih alat untuk menginisialisasi, setelah menjalankan alat beberapa lusin kali, dan setelah menjalankan beberapa lusin kali.

Berikut adalah hasil seperti yang ditunjukkan pada tampilan default LDGrapher (ukuran total): Grafik LDGrapher menunjukkan peningkatan penggunaan memori yang stabil

Inilah tumpukan panggilan untuk saluran merah: Tumpukan panggilan menunjukkan panggilan sg.dll ke fungsi SgsShapeFindRelation2

Seperti yang Anda lihat, SgsShapeFindRelation2fungsi dalam sg.dll tampaknya bertanggung jawab atas kebocoran memori.

Seperti yang saya mengerti, sg.dll adalah pustaka geometri inti yang digunakan oleh ArcObjects, dan SgsShapeFindRelation2mungkin di mana filter spasial sedang diterapkan.

Sebelum saya melakukan hal lain, saya hanya ingin melihat apakah ada orang lain yang mengalami masalah ini (atau sesuatu yang serupa) dan bagaimana jika ada sesuatu yang dapat mereka lakukan tentang hal itu. Juga apa yang bisa menjadi alasan terjadinya ini hanya dengan koneksi langsung? Apakah ini terdengar seperti bug di ArcObjects, masalah konfigurasi, atau masalah pemrograman?

Berikut ini adalah versi minimal dari metode yang menghasilkan perilaku ini:

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;
}

Ini kode solusi saya berdasarkan diskusi di bawah ini dengan 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;
}
blah238
sumber
1
+1 analisis hebat. Apakah Anda melihatnya hanya dengan koneksi langsung ?
Kirk Kuykendall
Baru saja mengujinya pada server yang lebih lama yang menggunakan koneksi aplikasi dan tidak ada kebocoran memori di sana. Jadi yakin sepertinya spesifik untuk koneksi langsung!
blah238
Versi ArcGIS apa (termasuk tingkat paket layanan)?
Philip
Klien: ArcGIS 10 SP2, Server: ArcGIS 9.3.1 SP1 (Saya pikir, akan memeriksa ulang besok).
blah238
Apakah tidak ada versi driver oracle yang perlu Anda pertimbangkan, sudah lama, tapi mungkin ODP.NET?
Kirk Kuykendall

Jawaban:

6

Ini terlihat seperti bug.

SG berisi perpustakaan geometri ArcSDE dan bukan perpustakaan geometri ArcObjects ... digunakan sebagai pra-filter sebelum tes menyentuh perpustakaan geometri ArcObjects.

Coba ini:

Abaikan baris ini:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

dan karena Anda tidak menyimpan referensi ke baris, Anda tidak perlu menggunakan kursor daur ulang, jadi alihkan bendera palsu ke true.

pCursor = (ICursor)pFeatureClass.Search(pSpatialFilter, true);

Anda akan melihat peningkatan dalam konsumsi memori dan kecepatan runtime. Namun demikian, jika bug masih mengenai, semoga ini akan menunda secara dramatis :)

Ragi Yaser Burhum
sumber
1
Terima kasih @Ragi - Saya mencoba kedua modifikasi tetapi tidak ada perubahan dalam tingkat memori yang bocor.
blah238
dapatkah Anda mencoba koneksi 2 tier (koneksi langsung) vs 3 tier (server aplikasi)? asalkan Anda memiliki server aplikasi yang sudah berjalan, ini hanya akan menjadi perubahan dalam string koneksi sde.
Ragi Yaser Burhum
oh, baru saja melihat komentar kirk, jadi ini adalah masalah koneksi langsung. IMHO, jika Anda melakukan ini dengan 3 tier, ada kemungkinan bahwa Anda akan melihat kebocoran di sisi server, tetapi klien akan tetap sama. Bolehkah saya bertanya apakah Anda melakukan sesuatu dengan pengeditan atau geometri kloning?
Ragi Yaser Burhum
1
Ya dan tidak. Mode 3 tier, giomgr tetap ada dan untuk setiap koneksi ia memunculkan proses gsrvr baru yang akan mati setelah Anda putuskan, jadi jika kebocoran ada di sana, itu akan hilang setelah Anda terputus. Selain itu, kami tidak dapat mengabaikan fakta bahwa koneksi langsung memang memiliki jalur kode yang sedikit berbeda ... Cobalah dua hal. Pertama, cukup matikan filter spasial sepenuhnya dan kembalikan fitur pertama lalu coba esriSpatialRelEnvelopeIntersects. Saya tahu bahwa secara semantik tidak ada yang sama, tetapi kami ingin melacak kebocorannya terlebih dahulu.
Ragi Yaser Burhum
3
Ya, jadi kedua metode ini menghindari panggilan sgShapeFindRelation2. Coba ini sekarang, esriSpatialRelEnvelopeIntersects pada filter spasial untuk membuat sde melakukan pra-penyaringan super dasar, dan kemudian ITopologicalOperator :: berpotongan untuk melakukan tes yang sebenarnya pada klien. Ini mungkin tidak seefisien sgShapeFindRelation2, tetapi itu akan menghindari memukul fungsi itu dan karenanya menghindari kebocoran.
Ragi Yaser Burhum
4

Jika ada yang masih tertarik dengan ini, itu diperbaiki di Versi 10.1.

Nomor Dukungan Teknis ESRI: NIM070156 dan NIM062420

http://support.esri.com/en/bugs/nimbus/TklNMDcwMTU2 http://support.esri.com/en/bugs/nimbus/TklNMDYyNDIw

travis
sumber
Itu tidak mencantumkan apa pun untuk Versi Tetap jadi saya kira saya hanya harus mengambil kata-kata Anda untuk itu. Saya belum diuji pada 10,1 sekalipun. Juga masalah dalam laporan bug saya tidak ada hubungannya dengan pelabelan jadi tidak yakin mengapa mereka menandainya sebagai duplikat dari yang lain.
blah238
1

Anda dapat mencoba pola berikut alih-alih try / finally { Marshal.FinalReleaseComObject(...) }:

using (ESRI.ArcGIS.ADF.ComReleaser cr) {
    var cursor = (ICursor) fc.Search(...);
    cr.ManageLifetime(cursor);
    // ...
}

Juga bekerja dengan Direct Connect, saya memiliki beberapa keberhasilan dalam loop dengan memaksakan System.GC.Collect()secara berkala (setiap begitu banyak iterasi), betapapun buruknya tampilannya.

David Holmes
sumber
Saya memang memberikan pendekatan ComReleaser menggunakan metode James MacKay untuk mendaur ulang kursor yang dijelaskan di sini: forums.arcgis.com/threads/... - itu tidak membuat perbedaan (itu hanya membungkus Marshal.ReleaseCOMObject, jadi tidak terlalu mengejutkan). Saya juga sudah mencoba menggunakan GC.Collect tetapi juga tidak berpengaruh. Saya seharusnya menyebutkan bahwa saya juga melihat memori yang dikelola menggunakan beberapa .NET profiler dan tidak satupun dari mereka menemukan objek yang dikelola atau memori yang dikelola menumpuk, menuntun saya untuk melihat memori yang tidak dikelola.
blah238