Xử lý rò rỉ bộ nhớ trong IFeatureClass.Search (chỉ trên SDE có kết nối trực tiếp) của ArcObjects?


16

Bộ phận hỗ trợ ESRI cho biết họ đã tái tạo vấn đề và đã mở một báo cáo lỗi (NIM070156).

Tôi đã xác định rằng có một rò rỉ bộ nhớ (trong bộ nhớ heap không được quản lý) xảy ra khi một công cụ trong bổ trợ ArcMap .NET / C # của tôi thực hiện một truy vấn không gian (trả về ICursortừ IFeatureClass.Searchmột ISpatialFilterbộ lọc truy vấn). Tất cả các đối tượng COM đang được phát hành ngay khi chúng không còn cần thiết (sử dụng Marshal.FinalReleaseCOMObject).

Để xác định điều này, trước tiên tôi thiết lập phiên PerfMon với các bộ đếm cho Private Byte, Virtual Byte và Work Set của ArcMap.exe và lưu ý rằng cả ba đều tăng đều đặn (khoảng 500KB mỗi lần lặp) với mỗi lần sử dụng công cụ thực hiện truy vấn . Điều quan trọng, điều này chỉ xảy ra khi được thực hiện đối với các lớp tính năng trên SDE bằng cách sử dụng kết nối trực tiếp (lưu trữ ST_Geometry, máy khách và máy chủ Oracle 11g). Các bộ đếm không đổi khi sử dụng cơ sở dữ liệu địa lý tệp, cũng như khi kết nối với một cá thể SDE cũ hơn sử dụng kết nối ứng dụng.

Sau đó, tôi đã sử dụng LeakDiagLDGograph (với một số hướng dẫn từ bài đăng trên blog này ) và đăng nhập Windows Heap Allocator ba lần: khi tôi tải ArcMap lần đầu tiên và chọn công cụ để khởi tạo nó, sau khi chạy công cụ vài chục lần và sau khi chạy công cụ nó vài chục lần nữa

Dưới đây là các kết quả như được hiển thị trong chế độ xem mặc định của LDGograph (tổng kích thước): Biểu đồ LDGograph cho thấy sự gia tăng ổn định trong việc sử dụng bộ nhớ

Đây là ngăn xếp cuộc gọi cho đường màu đỏ: Ngăn xếp cuộc gọi hiển thị cuộc gọi sg.dll đến chức năng SgsShapeFindRelation2

Như bạn có thể thấy SgsShapeFindRelation2chức năng trong sg.dll dường như là nguyên nhân gây ra rò rỉ bộ nhớ.

Theo tôi hiểu, sg.dll là thư viện hình học lõi được ArcObjects sử dụng và SgsShapeFindRelation2có lẽ là nơi bộ lọc không gian đang được áp dụng.

Trước khi tôi làm bất cứ điều gì khác, tôi chỉ muốn xem liệu có ai khác gặp phải vấn đề này (hoặc một cái gì đó tương tự) không và nếu có bất cứ điều gì họ có thể làm về vấn đề này. Ngoài ra những gì có thể là lý do cho điều này xảy ra chỉ với kết nối trực tiếp? Điều này có vẻ như là một lỗi trong ArcObjects, vấn đề cấu hình hoặc vấn đề lập trình?

Đây là phiên bản làm việc tối thiểu của phương thức tạo ra hành vi này:

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

Đây là mã giải pháp của tôi dựa trên cuộc thảo luận bên dưới với 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;
}

1
+1 phân tích tuyệt vời. Bạn có nhìn thấy nó chỉ với một kết nối trực tiếp ?
Kirk Kuykendall

Chỉ cần thử nghiệm nó trên một máy chủ cũ hơn sử dụng kết nối ứng dụng và không có rò rỉ bộ nhớ ở đó. Vì vậy, chắc chắn dường như cụ thể để kết nối trực tiếp!
blah238

Phiên bản nào của ArcGIS (bao gồm cấp gói dịch vụ)?
Philip

Máy khách: ArcGIS 10 SP2, Máy chủ: ArcGIS 9.3.1 SP1 (Tôi nghĩ, sẽ kiểm tra lại vào ngày mai).
blah238

Không có phiên bản trình điều khiển tiên tri nào bạn cần xem xét, đã được một thời gian, nhưng có lẽ ODP.NET?
Kirk Kuykendall

Câu trả lời:


6

Điều này trông giống như một lỗi.

SG chứa các thư viện hình học ArcSDE chứ không phải thư viện hình học ArcObjects ... nó được sử dụng làm bộ lọc trước khi thử nghiệm chạm vào thư viện hình học ArcObjects.

Thử đi:

Bỏ qua dòng này:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

và vì bạn không lưu tham chiếu đến hàng, nên bạn không cần phải sử dụng con trỏ tái chế, vì vậy hãy chuyển cờ giả thành true.

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

Bạn sẽ thấy một sự cải thiện cả về mức tiêu thụ bộ nhớ và tốc độ thời gian chạy. Tuy nhiên, nếu lỗi vẫn xảy ra, điều này hy vọng sẽ trì hoãn đáng kể :)


1
Cảm ơn @Ragi - Tôi đã thử cả hai sửa đổi nhưng không có thay đổi về tốc độ bộ nhớ bị rò rỉ.
blah238

bạn có thể thử kết nối 2 tầng (kết nối trực tiếp) với kết nối 3 tầng (máy chủ ứng dụng) không? với điều kiện bạn đã có máy chủ ứng dụng đang chạy, đây chỉ nên là một thay đổi trong chuỗi kết nối sde.
Ragi Yaser Burhum

oh, chỉ thấy bình luận của kirk, vì vậy nó là một vấn đề kết nối trực tiếp. IMHO, nếu bạn đã làm điều này với 3 tầng, có khả năng bạn sẽ thấy rò rỉ ở phía máy chủ, nhưng máy khách vẫn giữ nguyên. Tôi có thể hỏi nếu bạn đang làm bất cứ điều gì với chỉnh sửa hoặc nhân bản hình học?
Ragi Yaser Burhum

1
Vâng, có và không. Chế độ 3 tầng, giomgr vẫn tồn tại và với mỗi kết nối, nó sẽ sinh ra một quá trình gsrvr mới sẽ chết sau khi ngắt kết nối của bạn, vì vậy nếu rò rỉ ở đó, nó sẽ biến mất sau khi bạn ngắt kết nối. Ngoài ra, chúng tôi không thể giảm giá thực tế là kết nối trực tiếp có đường dẫn mã rất khác nhau ... Hãy thử hai điều. Thứ nhất, chỉ cần tắt hoàn toàn bộ lọc không gian và trả lại tính năng đầu tiên, sau đó thử chỉ esriSpatialRelEnvelIntersects. Tôi biết rằng về mặt ngữ nghĩa không ai trong số này giống nhau, nhưng chúng tôi muốn theo dõi rò rỉ đầu tiên.
Ragi Yaser Burhum

3
Vâng, vì vậy cả hai phương pháp đều tránh cuộc gọi sgShapeFindRelation2. Hãy thử ngay bây giờ, esriSpatialRelEnvelIntersects trên bộ lọc không gian để làm cho sde thực hiện một bộ lọc trước siêu cơ bản, sau đó ITopologicalOperator :: giao nhau để thực hiện kiểm tra thực tế trên máy khách. Điều này có thể không hiệu quả như sgShapeFindRelation2, nhưng nó sẽ tránh được chức năng đó và do đó tránh được sự rò rỉ.
Ragi Yaser Burhum

4

Nếu bất cứ ai vẫn quan tâm đến điều này, nó đã được sửa ở Phiên bản 10.1.

Số hỗ trợ kỹ thuật ESRI: NIM070156 và NIM062420

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


Nó không liệt kê bất cứ điều gì cho Phiên bản cố định vì vậy tôi đoán tôi sẽ phải nhận lời của bạn cho nó. Tôi chưa thử nghiệm ở 10.1. Ngoài ra, vấn đề trong báo cáo lỗi của tôi không liên quan gì đến việc dán nhãn nên không chắc tại sao họ lại đánh dấu nó là bản sao của cái kia.
blah238

1

Bạn có thể thử mẫu sau thay vì try / finally { Marshal.FinalReleaseComObject(...) }:

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

Cũng làm việc với Direct Connect, tôi đã có một số thành công trong các vòng lặp bằng cách buộc System.GC.Collect()định kỳ (mỗi lần lặp lại rất nhiều), tuy nhiên trông nó thật khó chịu.


Tôi đã cho ComReleaser một cách tiếp cận bằng cách sử dụng phương pháp của James MacKay để tái chế các con trỏ được mô tả ở đây: forum.arcgis.com/threads/ trộm - nó không tạo ra bất kỳ sự khác biệt nào (nó chỉ bao bọc Marshal.ReleaseCOMObject dù sao cũng không quá ngạc nhiên). Tôi cũng đã thử sử dụng GC.Collect nhưng nó cũng không có tác dụng. Tôi nên đề cập đến việc tôi cũng đã xem xét bộ nhớ được quản lý bằng cách sử dụng một vài trình biên dịch .NET và không ai trong số họ tìm thấy bất kỳ đối tượng được quản lý hoặc bộ nhớ được quản lý nào, dẫn đến tôi nhìn vào bộ nhớ không được quản lý.
blah238
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.