개체를 null 대 Dispose ()로 설정


108

저는 CLR과 GC가 작동하는 방식에 매료되었습니다 (C #, Jon Skeet의 서적 / 게시물 등을 통해 CLR을 읽음으로써 이에 대한 지식을 넓히고 있습니다).

어쨌든, 말하는 것의 차이점은 무엇입니까?

MyClass myclass = new MyClass();
myclass = null;

또는 MyClass에서 IDisposable 및 소멸자를 구현하고 Dispose ()를 호출하여?

또한 using 문이있는 코드 블록 (예 : 아래)이있는 경우 코드를 단계별로 실행하고 using 블록을 종료하면 개체가 폐기됩니까? 아니면 가비지 수집이 발생합니까? 어쨌든 using 블록에서 Dispose ()를 호출하면 어떻게됩니까?

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

스트림 클래스 (예 : BinaryWriter)에는 Finalize 메서드가 있습니까? 왜 그것을 사용하고 싶습니까?

답변:


210

폐기와 가비지 수집을 분리하는 것이 중요합니다. 그것들은 완전히 별개의 것들이며, 한 가지 공통점이 있습니다.

Dispose, 가비지 수집 및 마무리

using문 을 작성할 때 try / finally 블록에 대한 구문 설탕 일 뿐이므로 문 Dispose본문의 코드 using가 예외를 throw 하더라도 호출됩니다 . 이 없는 오브젝트가 블록의 끝에서 가비지 수집이 있음을 의미한다.

폐기는 관리되지 않는 리소스 (비 메모리 리소스)에 대한 것입니다. UI 핸들, 네트워크 연결, 파일 핸들 등이 될 수 있습니다. 이들은 제한된 리소스이므로 일반적으로 가능한 한 빨리 해제해야합니다. IDisposable유형이 관리되지 않는 리소스를 직접 (일반적으로를 통해 IntPtr) 또는 간접적으로 (예 : Stream, a SqlConnection등을 통해) "소유"할 때마다 구현해야합니다 .

가비지 수집 자체는 메모리에 관한 것입니다. 가비지 수집기는 더 이상 참조 할 수없는 개체를 찾아 해제 할 수 있습니다. 그래도 항상 가비지를 찾지는 않습니다. 필요한 것을 감지 할 때만 (예 : 힙의 한 "세대"가 메모리가 부족한 경우).

비틀기는 마무리 입니다. 가비지 수집기는 더 이상 도달 할 수 없지만 종료자가있는 개체 목록을 유지합니다 ( ~Foo()C # 에서처럼 다소 혼란스럽게 작성 됨 -C ++ 소멸자와 같지 않음). 메모리가 해제되기 전에 추가 정리를 수행해야하는 경우를 대비하여 이러한 개체에서 종료자를 실행합니다.

종료자는 해당 유형의 사용자가 규칙적으로 처리하는 것을 잊은 경우 리소스를 정리하는 데 거의 항상 사용됩니다. 그래서 당신은을 열면 FileStream하지만 전화하는 것을 잊지 Dispose또는 Close종료 자는 것, 결국 당신을위한 기본 파일 핸들을 해제. 잘 작성된 프로그램에서 파이널 라이저는 제 생각에는 거의 실행되지 않아야합니다.

변수 설정 null

변수 설정에 대한 한 가지 작은 포인트 null는 가비지 수집을 위해 거의 필요하지 않습니다. 내 경험상 객체의 "일부"가 더 이상 필요하지 않은 경우는 드물지만 멤버 변수 인 경우 가끔 수행 할 수 있습니다. 지역 변수 인 경우 JIT는 일반적으로 참조를 다시 사용하지 않을 때를 알 수있을만큼 충분히 똑똑합니다 (릴리스 모드에서). 예를 들면 :

StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();

// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);

// These aren't helping at all!
x = null;
sb = null;

// Assume that x and sb aren't used here

이 한 시간 에 지역 변수를 설정하는 가치는 null당신이 루프에있어, 루프의 필요성을 일부 가지 변수를 사용하는 경우입니다 그러나 당신은 당신이 안되는 지점에 도달했습니다 알고있다. 예를 들면 :

SomeObject foo = new SomeObject();

for (int i=0; i < 100000; i++)
{
    if (i == 5)
    {
        foo.DoSomething();
        // We're not going to need it again, but the JIT
        // wouldn't spot that
        foo = null;
    }
    else
    {
        // Some other code 
    }
}

IDisposable / finalizer 구현

그렇다면 자신의 유형이 종료자를 구현해야합니까? 거의 확실하지 않습니다. 관리되지 않는 리소스 만 간접적으로 보유하는 경우 (예 : FileStream멤버 변수가있는 경우) 자체 종료자를 추가하는 것은 도움이되지 않습니다. 객체가있을 때 스트림은 거의 확실하게 가비지 수집에 적합하므로 신뢰할 수 있습니다. FileStream종료 자 (필요한 경우-다른 것을 참조 할 수 있음)가 있습니다. 관리되지 않는 리소스를 "거의"직접 보유하고 싶다면 SafeHandle친구가 되세요. 진행하는 데 약간의 시간이 걸리지 만 종료 자를 다시 작성할 필요가 거의 없음을 의미 합니다 . 일반적으로 리소스 (an IntPtr) 에 대한 직접적인 핸들이 있고 다음으로 이동해야하는 경우에만 종료자가 필요 합니다.SafeHandle가능한 한 빨리. (두 개의 링크가 있습니다. 이상적으로는 둘 다 읽으십시오.)

Joe Duffy는 읽을 가치가있는 종료 자 와 IDisposable (많은 똑똑한 사람들과 공동으로 작성)에 대한 매우 긴 지침을 가지고 있습니다. 클래스를 봉인하면 삶이 훨씬 쉬워진다는 사실을 알아 두는 것이 좋습니다 Dispose. 새로운 가상 Dispose(bool)메서드 를 호출하도록 재정의하는 패턴 등은 클래스가 상속을 위해 설계된 경우에만 관련이 있습니다.

이것은 약간 엉망 이었지만, 원하는 부분에 대한 설명을 요청하십시오. :)


다시 "지역 변수를 null로 설정할 가치가있는 한 번"-아마도 더 까다로운 "캡처"시나리오 (동일한 변수의 여러 캡처)도 있지만 게시물을 복잡하게 만들 가치가 없을 수도 있습니다! +1 ...
Marc Gravell

@Marc : 맞습니다-저는 캡처 된 변수에 대해 생각조차하지 않았습니다. 흠. 그래, 나는 그것을 내버려 둘 것이라고 생각한다;)
Jon Skeet

위의 코드 스 니펫에서 "foo = null"을 설정하면 어떤 일이 발생하는지 말씀해 주시겠습니까? 내가 아는 한, 그 줄은 관리되는 힙에서 foo 개체를 가리키는 변수의 값만 지 웁니다. 그래서 질문은 거기에서 foo 객체는 어떻게 될까요? 폐기라고 부르지 않습니까?
odiseh 2010

@odiseh : 개체가 일회용이면 예-폐기해야합니다. 답변의 해당 섹션은 완전히 분리 된 가비지 컬렉션 만 다루었습니다.
Jon Skeet

1
IDisposable 문제에 대한 설명을 찾고 있었기 때문에 "IDisposable Skeet"을 검색하여이 사실을 발견했습니다. 큰! : D
Maciej Wozniak 2015 년

22

개체를 삭제하면 리소스가 해제됩니다. 변수에 null을 할당하면 참조를 변경하는 것입니다.

myclass = null;

이것을 실행 한 후에도 myclass가 참조하는 객체는 여전히 존재하며 GC가이를 정리할 때까지 계속됩니다. Dispose가 명시 적으로 호출되거나 using 블록에있는 경우 가능한 한 빨리 모든 리소스가 해제됩니다.


7
그것은 여전히 줄을 실행 한 후 존재하지 - 그것은 된 쓰레기는 수집 한 수 있습니다 전에 그 라인. JIT는 영리합니다. 이와 같은 라인은 거의 항상 관련성이 없습니다.
Jon Skeet

6
null로 설정하면 개체가 보유한 리소스가 해제 되지 않을 수 있습니다. GC는 처리하지 않고 종료 만 수행하므로 개체가 관리되지 않는 리소스를 직접 보유하고 종료자가 처리되지 않거나 종료자가없는 경우 해당 리소스가 누출됩니다. 알아야 할 것.
LukeH

6

두 작업은 서로 관련이 많지 않습니다. 참조를 null로 설정하면 간단히 수행됩니다. 그 자체로는 참조 된 클래스에 전혀 영향을주지 않습니다. 변수는 더 이상 이전에 사용하던 객체를 가리 키지 않지만 객체 자체는 변경되지 않습니다.

Dispose ()를 호출하면 객체 자체에 대한 메서드 호출입니다. Dispose 메서드가 수행하는 작업은 이제 개체에서 수행됩니다. 그러나 이것은 객체에 대한 참조에 영향을 미치지 않습니다.

중복되는 유일한 영역은 개체에 대한 참조가 더 이상 없을 결국 가비지 수집 된다는 것 입니다. 그리고 클래스가 IDisposable 인터페이스를 구현하면 가비지 수집되기 전에 개체에서 Dispose ()가 호출됩니다.

그러나 두 가지 이유로 참조를 null로 설정 한 직후에는 발생하지 않습니다. 첫째, 다른 참조가 존재할 수 있으므로 아직 가비지 수집을 전혀받지 못할 것입니다. 둘째, 이것이 마지막 참조 였더라도 이제 가비지 수집 준비가되었습니다. 가비지 수집기가 삭제를 결정할 때까지 아무 일도 일어나지 않습니다. 목적.

개체에 대해 Dispose ()를 호출해도 개체가 어떤 식 으로든 "죽이는"것은 아닙니다. 일반적으로 개체 나중에 안전하게 삭제할 있도록 정리하는 데 사용 되지만 궁극적으로 Dispose에는 마법 같은 것이 없으며 클래스 메서드 일뿐입니다.


이 답변은 "재귀 적"의 답변에 대한 칭찬 또는 세부 사항이라고 생각합니다.
dance2die
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.