메모리 - c# using 사용법



IDisposable 인터페이스의 올바른 사용 (13)

Dispose 패턴의 목적은 관리되는 리소스와 관리되지 않는 리소스를 모두 정리하는 메커니즘을 제공하고 해당 리소스가 Dispose 메서드가 호출되는 방식에 따라 다릅니다. 예를 들어, Dispose를 사용하면 실제로 처분과 관련된 작업을 수행하지 않습니다. 목록을 지우면 처분중인 컬렉션에 아무런 영향을 미치지 않기 때문입니다. 마찬가지로 변수를 null로 설정하는 호출도 GC에 영향을 미치지 않습니다.

Dispose 패턴을 구현하는 방법에 대한 자세한 내용은이 article 를 참조하십시오. 기본적으로 다음과 같습니다.

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            // Dispose unmanaged managed resources.

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

여기에서 가장 중요한 방법은 실제로 두 가지 상황에서 실행되는 Dispose (bool)입니다.

  • disposing == true : 메서드는 사용자 코드에 의해 직접 또는 간접적으로 호출되었습니다. 관리되는 자원과 관리되지 않는 자원을 처리 할 수 ​​있습니다.
  • disposing == false : finalizer 내부에서 런타임에 메서드가 호출되었으므로 다른 객체를 참조하면 안됩니다. 관리되지 않는 리소스 만 삭제할 수 있습니다.

GC가 정리 작업을 처리하게하는 문제는 GC가 수집주기 (GC.Collect ()를 호출 할 수 있지만 실제로는 사용하지 않아야 함)를 실행할 시간에 대한 실질적인 제어권이 없다는 것입니다. 필요 이상으로 오래갑니다. Dispose ()를 호출하면 실제로 수집주기가 발생하지 않으며 어떤 방식 으로든 GC가 객체를 수집 / 해제하게합니다. 단순히 사용 된 자원을보다 결정 론적으로 정리하는 수단을 제공하고 GC에이 정리가 이미 수행되었음을 알립니다.

IDisposable 및 처분 패턴의 전체적인 포인트는 메모리를 즉시 해제하는 것이 아닙니다. Dispose에 대한 호출이 메모리를 즉시 해제 할 수있는 유일한 기회는 처리 == false 시나리오를 처리하고 관리되지 않는 리소스를 조작하는 경우입니다. 관리 코드의 경우 GC가 수집주기를 실행할 때까지 메모리가 실제로 회수되지 않습니다. GC.Collect ()를 호출하는 것 외에는 제어 할 수 없습니다. 이미 언급했듯이 좋은 아이디어는 아닙니다.

.NET의 문자열은 unamanged 리소스를 사용하지 않으며 IDisposable을 구현하지 않으므로 시나리오가 실제로 유효하지 않습니다. 강제로 "정리"할 수는 없습니다.

https://src-bin.com

IDisposable 인터페이스의 "기본"사용은 관리되지 않는 리소스를 정리하는 것이라는 MSDN 설명서 를 읽었습니다.

나에게 "관리되지 않는"은 데이타베이스 연결, 소켓, 창 핸들 등과 같은 것을 의미합니다.하지만 가비지 컬렉터는 관리 자원을 해제하기 위해 Dispose() 메소드가 구현 된 코드를 보았습니다. 그걸 돌봐 줘.

예 :

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }

내 질문은,이 가비지 컬렉터가 MyCollection 일반적으로 사용하는 것보다 빠르게 사용 가능한 메모리를 MyCollection 것입니까?

편집 : 지금까지 사람들은 IDisposable을 사용하여 데이터베이스 연결 및 비트 맵과 같은 관리되지 않는 리소스를 정리하는 좋은 예를 게시했습니다. 그러나 위 코드의 _theList 에 백만 개의 문자열이 포함되어 있다고 가정하면 가비지 수집기를 기다리는 대신 메모리를 지금 _theList 것이 좋습니다. 위의 코드가이를 수행합니까?


Answer #1

Dispose가 호출 된 후에도 객체의 메소드를 더 이상 호출 할 수 없습니다 (객체가 이후의 Dispose 호출을 허용해야 함). 그러므로 그 질문의 예는 어리 석다. Dispose가 호출되면 객체 자체를 삭제할 수 있습니다. 따라서 사용자는 전체 객체에 대한 모든 참조를 폐기해야합니다 (null로 설정). 그러면 내부의 모든 관련 객체가 자동으로 정리됩니다.

관리되는 / 관리되지 않는 일반적인 질문과 다른 답변의 토론과 관련하여이 질문에 대한 답변은 관리되지 않는 리소스의 정의부터 시작해야한다고 생각합니다.

이 기능이 중요하다는 것은 시스템을 상태로 만들기 위해 호출 할 수있는 함수가 있으며, 그 상태에서 다시 가져올 수있는 다른 함수가 있다는 것입니다. 이제는 전형적인 예에서 첫 번째 함수는 파일 핸들을 반환하는 함수이고 두 번째 함수는 CloseHandle 호출하는 함수입니다.

그러나 이것이 핵심 요소입니다. 일치하는 함수 쌍일 수 있습니다. 하나는 국가를 건설하고, 다른 하나는 국가를 붕괴시킵니다. 상태가 구축되었지만 해체되지 않은 경우 자원 인스턴스가 존재합니다. 적법한시기에 티 해드를 준비해야합니다. 리소스는 CLR에서 관리하지 않습니다. 유일하게 자동 관리되는 자원 유형은 메모리입니다. GC와 스택의 두 종류가 있습니다. 값 유형은 스택에 의해 관리되거나 참조 유형 내에서 타기에 의해 관리되며 참조 유형은 GC에 의해 관리됩니다.

이러한 함수는 자유롭게 끼워 넣을 수있는 상태 변경을 초래하거나 완벽하게 중첩되어야 할 수 있습니다. 상태 변경은 스레드 안전성이거나 그렇지 않을 수 있습니다.

법무부의 질문에있는 예를보십시오. 로그 파일의 들여 쓰기에 대한 변경 사항은 완전히 중첩되어 있어야하며 그렇지 않으면 모두 잘못됩니다. 또한 스레드가 안전하지 않을 수도 있습니다.

관리되지 않는 리소스를 정리하기 위해 가비지 수집기를 사용하여 타기 할 수 있습니다. 그러나 상태 변경 함수가 스레드 안전하고 두 상태가 어떤 방식 으로든 겹치는 수명 만있을 수 있습니다. 그래서 자원의 Justice의 예제는 finalizer가 없어야합니다! 누구에게만 도움이되지 않습니다.

이러한 리소스의 경우 finalizer없이 IDisposable 구현할 수 있습니다. finalizer는 절대적으로 선택 사항입니다. 이것은 많은 책들에서 언급되었거나 언급되지 않은 것입니다.

그런 다음 using 문을 using Dispose 가 호출되도록 할 수 있습니다. 이것은 본질적으로 스택과 함께 타기와 같은 것입니다 (파이널 라이저가 GC에 있으므로 스택을 using 입니다).

누락 된 부분은 수동으로 Dispose를 작성하고 필드와 기본 클래스를 호출해야한다는 것입니다. C ++ / CLI 프로그래머는 그렇게 할 필요가 없습니다. 컴파일러는 대부분의 경우이를 위해이를 씁니다.

내가 둥지를 완벽하게 구현하고 스레드 세이프가 아닌 (IDisposable을 구현하는 모든 클래스에 파이널 라이저를 추가 할 수없는 사람과 논쟁을 벌일 때의 문제를 피하기 위해) 다른 대안이 있습니다. .

클래스를 작성하는 대신 함수를 작성합니다. 이 함수는 대리자가 다음을 호출 할 수 있도록 허용합니다.

public static void Indented(this Log log, Action action)
{
    log.Indent();
    try
    {
        action();
    }
    finally
    {
        log.Outdent();
    }
}

그리고 간단한 예가 있습니다 :

Log.Write("Message at the top");
Log.Indented(() =>
{
    Log.Write("And this is indented");

    Log.Indented(() =>
    {
        Log.Write("This is even more indented");
    });
});
Log.Write("Back at the outermost level again");

전달되는 람다는 코드 블록의 역할을하므로 호출자가 악용 할 위험이 더 이상 없다는 것을 제외하고는 자신이 using 하는 것과 동일한 목적을 수행하는 자체 제어 구조를 만드는 것과 같습니다. 자원을 정리하는 데 실패 할 수있는 방법은 없습니다.

이 기술은 리소스가 A, B 리소스를 빌드 한 다음 A 리소스를 죽인 다음 나중에 B 리소스를 죽일 수 있기를 원하기 때문에 수명이 중복되는 종류 인 경우 유용하지 않습니다. 당신이 이처럼 완벽하게 둥지를 틀도록 강요 한 경우. 그러나 당신은 IDisposable 을 사용해야합니다 (그러나 스레드 세이프티를 구현하지 않는 한 파이널 라이저가 없어도 무료입니다).


Answer #2

시나리오 IDisposable : 관리되지 않는 리소스 정리, 이벤트 등록 취소, 연결 닫기

IDisposable ( threadsafe가 아닌 )을 구현하는 데 사용하는 관용구 :

class MyClass : IDisposable {
    // ...

    #region IDisposable Members and Helpers
    private bool disposed = false;

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing) {
        if (!this.disposed) {
            if (disposing) {
                // cleanup code goes here
            }
            disposed = true;
        }
    }

    ~MyClass() {
        Dispose(false);
    }
    #endregion
}

Answer #3

어쨌든 MyCollection 을 가비지 수집하려면 처리 할 필요가 없습니다. 이렇게하면 CPU를 필요한만큼만 휘젓다가 가비지 수집기가 이미 수행 한 사전 계산 분석을 무효화 할 수도 있습니다.

IDisposable 을 사용하여 관리되지 않는 리소스와 함께 스레드가 올바르게 배치되는지 확인합니다.

편집 스콧의 의견에 대한 응답으로 :

GC 성능 메트릭이 영향을받는 유일한 시간은 [호출] GC.Collect () 호출이 "

개념적으로, GC는 객체 참조 그래프의 뷰와 스레드 스택 프레임에서 모든 참조를 유지합니다. 이 힙은 꽤 크고 많은 메모리 페이지에 걸쳐있을 수 있습니다. 최적화로서 GC는 자주 변경되지 않는 페이지 분석을 캐시하여 페이지를 불필요하게 다시 스캔하지 않도록합니다. GC는 페이지의 데이터가 변경되면 커널에서 알림을 수신하므로 페이지가 더럽고 다시 스캔해야한다는 것을 알고 있습니다. 컬렉션이 Gen0에있는 경우 페이지의 다른 항목도 변경 될 가능성이 있지만 Gen1 및 Gen2에서는 그렇지 않습니다. Anecdotally,이 후크는 Mac OS X에서 GC를 Mac으로 이식 한 팀이 Silverlight 플러그인을 해당 플랫폼에서 작동시킬 수 없었습니다.

불필요한 자원 처리에 대한 또 다른 포인트 : 프로세스가 언로드되는 상황을 상상해보십시오. 프로세스가 얼마 동안 실행되었다고 상상해보십시오. 프로세스의 메모리 페이지 중 많은 부분이 디스크로 스왑되었을 가능성이 있습니다. 적어도 그들은 L1 또는 L2 캐시에 있지 않습니다. 이러한 상황에서 프로세스가 종료 될 때 운영 체제에 의해 해제 될 리소스를 '릴리스'하기 위해 모든 데이터 및 코드 페이지를 다시 메모리로 스왑하기 위해 언로드하는 응용 프로그램에 대한 요점은 없습니다. 이는 관리되는 리소스와 관리되지 않는 특정 리소스에도 적용됩니다. 배경이 아닌 스레드를 유지하는 자원 만 폐기해야합니다. 그렇지 않으면 프로세스가 활성 상태로 유지됩니다.

이제 정상적인 실행 중에 관리되지 않는 메모리 누수를 피하기 위해 일시적 리소스가 올바르게 정리되어야합니다 (@fezmonkey가 데이터베이스 연결, 소켓, 창 핸들 을 가리킴). 이것들은 처분되어야 할 것들입니다. 만약 당신이 스레드를 소유하고있는 어떤 클래스를 생성했다면 (그리고 소유하고 있다는 의미로 작성한 것이므로 적어도 코딩 스타일에 의해 멈춤을 보장 할 책임이있다), 그 클래스는 IDisposable 구현하고 스레드를 해체해야한다. Dispose .

.NET 프레임 워크는 IDisposable 인터페이스를 신호로 사용하여 개발자에게이 클래스를 삭제 해야 한다는 경고를 표시 합니다 . 프레임 워크에서 IDisposable (명시 적 인터페이스 구현 제외)을 구현하는 모든 유형을 IDisposable 수 없습니다. 처분은 선택적입니다.


Answer #4

IDisposable 이벤트 구독을 취소하는 데 좋습니다.


Answer #5

관리 리소스를 폐기하는 가장 정당한 사용 사례는 GC가 수집되지 않는 리소스를 회수 할 수 있도록 준비하는 것입니다.

가장 좋은 예는 순환 참조입니다.

순환 참조를 피하는 패턴을 사용하는 것이 가장 좋지만 '부모'에 대한 참조가있는 (예를 들어) '자식'객체로 끝나면 방치하면 부모의 GC 수집을 중지 할 수 있습니다 레퍼런스와 GC에 의존 - 플러스 파이널 라이저를 구현했다면 결코 호출되지 않을 것입니다.

이 방법을 사용하는 유일한 방법은 자식에 대한 부모 참조를 null로 설정하여 순환 참조를 수동으로 끊는 것입니다.

IDisposable을 부모 및 자식에 구현하는 것이 가장 좋은 방법입니다. Dispose가 부모에 대해 호출되고 모든 자식에 대해 Dispose를 호출하고 자식 Dispose 메서드에 부모 참조를 null로 설정합니다.


Answer #6

Dispose()예제 코드에서 연산 이하는 것들이 있는데 , 이는 객체 의 일반적인 GC로 인해 발생하지 않는 효과를 가질 있습니다 MyCollection.

가 참조하는 객체 경우 _theList또는이 _theDict다른 객체에 의해 참조된다, 그 List<>또는 Dictionary<>개체가 컬렉션에 적용 대상이되지 않는다하지만 내용을 갑자기이 없습니다. 예제와 같이 Dispose () 작업이 없으면 해당 컬렉션에는 여전히 해당 내용이 포함됩니다.

물론,이 상황에 내가 깨진 디자인을 부를 것이다했다 경우 - 것을 난 그냥 지적하고 있습니다 (pedantically, 내 생각) Dispose()작업은 다른 용도가 있는지 여부에 따라 완전하게 중복되지 않을 수도 있습니다 List<>또는 Dictionary<>수 없습니다 조각에 표시됩니다.


Answer #7

게시 한 예제에서는 여전히 "메모리를 지금 비우지"않습니다. 모든 메모리는 가비지 수집되지만 이전 generation 에 메모리를 수집 할 수 있습니다 . 확신하기 위해 몇 가지 테스트를 실행해야합니다.

프레임 워크 설계 지침은 규칙이 아니라 지침입니다. 인터페이스가 주로 사용하는 용도, 사용시기, 사용 방법 및 사용하지 않을시기를 알려줍니다.

나는 일단 IDisposable을 사용하여 오류가 발생하면 간단한 RollBack () 코드를 읽습니다. 아래의 MiniTx 클래스는 Dispose ()에서 플래그를 확인하고 Commit호출이 발생하지 않으면 Rollback자체 호출 합니다. 그것은 간접적 인 계층을 추가하여 호출 코드를 이해하고 유지하는 것을 훨씬 쉽게 해줍니다. 결과는 다음과 같습니다.

using( MiniTx tx = new MiniTx() )
{
    // code that might not work.

    tx.Commit();
} 

나는 타이밍 / 로깅 코드도 똑같은 것을 보았다. 이 경우 Dispose () 메서드는 타이머를 중지하고 블록이 종료되었음을 기록합니다.

using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
    // code to time...
}

따라서 관리되지 않는 리소스 정리는 수행하지 않지만 IDisposable을 사용하여 코드를보다 명확하게 만드는 몇 가지 구체적인 예가 있습니다.


Answer #8

그래, 그 코드는 완전히 중복되고 불필요하며 가비지 컬렉터가 그렇지 않으면 할 수없는 어떤 일도하지 않습니다. (일단 MyCollection의 인스턴스가 범위를 벗어나면 특히 그렇습니다.) 특히 .Clear()호출.

귀하의 편집에 대한 답변 : 정렬. 내가 이렇게하면 :

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

메모리 관리 목적으로이 기능과 기능면에서 동일합니다.

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

정말로 정말로이 순간 메모리를 해제해야한다면, 전화하십시오 GC.Collect(). 하지만 여기서이 작업을 수행 할 이유는 없습니다. 필요할 때마다 메모리가 해제됩니다.



Answer #10

별도로 제어 할 수있는 방법으로는 주 사용으로 수명시스템 리소스를 (완전히의 멋진 대답이 적용 이안 , 명성을!)에 는 IDisposable / 사용 콤보 것은도에 사용할 수있는 범위 (중요) 글로벌 자원의 상태 변화 : 콘솔스레드처리 , 어떤 전역 객체 같은 응용 프로그램 인스턴스 .

이 패턴에 대한 기사를 작성했습니다 : http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/

콘솔 색상 , 현재 스레드 문화 , Excel 응용 프로그램 객체 속성 등 재사용이 가능 하고 읽기 쉬운 방식으로 자주 사용되는 전역 상태를 보호하는 방법을 보여줍니다 .


Answer #11

어떤 것이 든, 나는 그것을 버릴 때보 다 효율적인 것으로 기대 한다.

Clear () 메서드를 호출하는 것은 불필요하며 GC는 Dispose가 처리하지 않으면 그렇게하지 않을 것입니다 ...


Answer #12

첫 번째 정의. 나를 위해 관리되지 않는 리소스는 IDisposable 인터페이스 또는 dll 호출을 사용하여 생성 된 무언가를 구현하는 일부 클래스를 의미합니다. GC는 그러한 객체를 다루는 방법을 모른다. 클래스에 값 유형 만있는 경우이 클래스를 관리되지 않는 리소스가있는 클래스로 간주하지 않습니다. 내 코드는 다음 사례를 따른다.

  1. 나 클래스에 의해 생성 된 클래스가 관리되지 않는 리소스를 사용한다면 메모리를 정리하기 위해 IDisposable 인터페이스를 구현해야 함을 의미합니다.
  2. 사용이 끝나자 마자 개체를 청소하십시오.
  3. 내 처분 방법에서는 클래스의 모든 IDisposable 멤버를 반복하고 Dispose를 호출합니다.
  4. 내 Dispose 메서드에서 GC.SuppressFinalize (this)를 호출하여 가비지 수집기에 내 개체가 이미 정리되었음을 알립니다. GC 호출은 비용이 많이 드는 작업이기 때문에이 작업을 수행합니다.
  5. 추가주의 사항으로 Dispose ()를 여러 번 호출 할 수 있도록 노력합니다.
  6. 때로는 private 멤버 _disposed를 추가하고 객체가 정리 된 메소드 호출을 체크 인합니다. 그리고 그것이 정리 되었다면 ObjectDisposedException 을 생성합니다.
    다음 템플릿은 제가 단어의 코드 샘플로 설명한 것을 보여줍니다 :

public class SomeClass : IDisposable
    {
        /// <summary>
        /// As usually I don't care was object disposed or not
        /// </summary>
        public void SomeMethod()
        {
            if (_disposed)
                throw new ObjectDisposedException("SomeClass instance been disposed");
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private bool _disposed;

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;
            if (disposing)//we are in the first call
            {
            }
            _disposed = true;
        }
    }




idisposable