Arceature의 IFeatureClass.Search (직접 연결이있는 SDE에서만)에서 메모리 누수를 해결합니까?


16

ESRI 지원팀은 문제를 재현했으며 버그 보고서 (NIM070156)를 열었다 고 말합니다.

.NET / C # ArcMap 애드 인의 도구가 공간 쿼리 ( 쿼리 필터 ICursorIFeatureClass.Search사용하여 반환)를 수행 할 때 발생하는 메모리 누수 (관리되지 않는 힙 메모리에서)가 ISpatialFilter있음을 확인했습니다. 더 이상 필요하지 않은 즉시 모든 COM 개체가 해제됩니다 (사용 Marshal.FinalReleaseCOMObject).

이를 확인하기 위해 먼저 ArcMap.exe의 Private Bytes, Virtual Bytes 및 Working Set에 대한 카운터를 사용하여 PerfMon 세션을 설정 하고 쿼리를 수행하는 도구를 사용할 때마다 세 가지 모두 꾸준히 증가합니다 (반복 당 약 500KB). . 결정적으로 이것은 직접 연결 (ST_Geometry 스토리지, Oracle 11g 클라이언트 및 서버)을 사용하여 SDE의 기능 클래스에 대해 수행 될 때만 발생합니다 . 파일 지오 데이터베이스를 사용할 때와 응용 프로그램 연결을 사용하는 이전 SDE 인스턴스에 연결할 때 카운터는 일정하게 유지되었습니다.

그런 다음 LeakDiagLDGrapher (이 블로그 게시물의 지침과 함께 )를 사용하고 세 번에 Windows 힙 할당자를 기록했습니다. 처음 ArcMap을로드하고 도구를 선택하여 도구를 몇 번 실행 한 후 실행 한 후 몇 번 더.

LDGrapher의 기본보기 (총 크기)에 표시된 결과는 다음과 같습니다. 메모리 사용량이 꾸준히 증가하는 LDGrapher 그래프

빨간색 선의 호출 스택은 다음과 같습니다. SgsShapeFindRelation2 함수에 대한 sg.dll 호출을 보여주는 호출 스택

보시다시피 SgsShapeFindRelation2sg.dll 의 기능은 메모리 누수를 담당하는 것으로 보입니다.

내가 이해하는 것처럼 sg.dll은 ArcObjects가 사용하는 핵심 지오메트리 라이브러리 SgsShapeFindRelation2이며 아마도 공간 필터가 적용되는 곳 일 것입니다.

다른 작업을 수행하기 전에 다른 사람 이이 문제 (또는 비슷한 것)에 부딪 쳤는지, 그들이 할 수있는 일이 무엇인지보고 싶었습니다. 또한 직접 연결로만 발생하는 이유는 무엇입니까? ArcObjects의 버그, 구성 문제 또는 프로그래밍 문제처럼 들립니까?

다음은이 동작을 생성하는 최소한의 작업 버전입니다.

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

다음은 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. 직접 연결 로만보고 있습니까?
커크 Kuykendall

응용 프로그램 연결을 사용하는 이전 서버에서 테스트했으며 메모리 누수가 없습니다. 따라서 직접 연결에만 적용되는 것 같습니다!
blah238

어떤 버전의 ArcGIS (서비스 팩 수준 포함)?
Philip

클라이언트 : ArcGIS 10 SP2, 서버 : ArcGIS 9.3.1 SP1 (내일 다시 확인할 것입니다).
blah238

고려해야 할 오라클 드라이버 버전이 없습니까? 오랜 시간이지만 ODP.NET입니까?
커크 Kuykendall

답변:


6

이것은 버그처럼 보입니다.

SG에는 ArcObjects 지오메트리 라이브러리가 아닌 ArcSDE 지오메트리 라이브러리가 포함되어 있습니다. 테스트가 ArcObjects 지오메트리 라이브러리에 도달하기 전에 사전 필터로 사용됩니다.

이 시도:

이 줄을 생략하십시오 :

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

행에 대한 참조를 저장하지 않기 때문에 리사이클 커서를 사용할 필요가 없으므로 false 플래그를 true로 전환하십시오.

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

메모리 소비와 런타임 속도가 모두 향상되었습니다. 그럼에도 불구하고, 버그가 여전히 발생하면, 이것은 희망적으로 극적으로 지연 될 것입니다 :)


1
감사합니다 @ Ragi-두 가지 수정을 시도했지만 메모리 누수 속도에는 변화가 없었습니다.
blah238

2 계층 (직접 연결)과 3 계층 (애플리케이션 서버) 연결을 시도 할 수 있습니까? Application Server가 이미 실행중인 경우 sde 연결 문자열 만 변경해야합니다.
Ragi Yaser Burhum

오, 방금 kirk의 의견을 보았으므로 직접적인 연결 문제입니다. IMHO, 3 계층 으로이 작업을 수행하면 서버 측에서 누수가 발생할 가능성이 있지만 클라이언트는 동일하게 유지됩니다. 편집 세션 또는 복제 형상으로 작업하고 있는지 물어볼 수 있습니까?
Ragi Yaser Burhum

1
예, 아니오 3 계층 모드 인 giomgr은 상주 상태를 유지하며 모든 연결에 대해 연결을 끊은 후에 죽을 새로운 gsrvr 프로세스를 생성하므로 누출이 발생한 경우 연결을 끊은 후에 사라집니다. 또한 직접 연결에는 코드 경로가 약간 다르다는 사실을 무시할 수 없습니다. 두 가지를 시도하십시오. 첫째, 공간 필터를 완전히 끄고 첫 번째 기능을 반환 한 다음 esriSpatialRelEnvelopeIntersects를 시도하십시오. 의미 적으로 동일한 것은 없지만, 누출을 먼저 추적하고 싶습니다.
Ragi Yaser Burhum

3
예, 두 방법 모두 sgShapeFindRelation2 호출을 피합니다. 이제 esriSpatialRelEnvelopeIntersects를 공간 필터에서 사용하여 sde가 수퍼 기본 사전 필터링을 수행 한 다음 ITopologicalOperator :: intersect를 사용하여 클라이언트에서 실제 테스트를 수행하십시오. 이는 sgShapeFindRelation2만큼 효율적이지 않을 수 있지만 해당 기능에 부딪히지 않아 누출을 피할 수 있습니다.
Ragi Yaser Burhum

4

여전히 관심이있는 사람은 버전 10.1로 수정되었습니다.

ESRI 기술 지원 번호 : NIM070156 및 NIM062420

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


버전 고정에 대해서는 아무것도 나열하지 않으므로 단어를 가져와야합니다. 그래도 10.1에서 테스트하지 않았습니다. 또한 내 버그 보고서의 문제는 레이블과 관련이 없으므로 왜 다른 버그의 복제본으로 표시했는지 확실하지 않습니다.
blah238

1

대신 다음 패턴을 시도 할 수 있습니다 try / finally { Marshal.FinalReleaseComObject(...) }.

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

Direct Connect와 함께 일하면서 System.GC.Collect()주기적으로 (모든 반복 작업 을 강제로) 반복하여 루프에서 성공 했지만 불쾌한 것처럼 보입니다.


나는 ComReleaser가 커서 여기에 설명 재활용 제임스 맥케이의 방법을 사용하여 샷에 접근 제공했다 : forums.arcgis.com/threads/...를 - 그것은 어떤 차이를 만들지 않았다 (그냥 Marshal.ReleaseCOMObject 어쨌든 너무 너무 놀라운 일이 아니다 랩). 나는 또한 GC.Collect를 사용해 보았지만 효과가 없었다. 필자는 두 개의 .NET 프로파일 러를 사용하여 관리되는 메모리를 살펴 봤지만 관리 대상 개체 또는 관리되는 메모리가 발견되지 않아 관리되지 않는 메모리를 보지 못했음을 언급해야합니다.
blah238
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.