.NET에서 사용한 후 개체를 Null / Nothing으로 설정


187

작업이 끝나면 모든 개체를 null( NothingVB.NET에서) 로 설정해야합니까 ?

.NET에서는 IDisposable일부 자원을 해제 하기 위해 인터페이스를 구현하는 객체 인스턴스를 처분해야한다는 것을 이해합니다. 하지만 객체는 폐기 된 후에도 여전히 무언가 일 수 있으므로 (따라서 isDisposed양식 의 속성) 여전히 상주 할 수 있다고 가정합니다 기억이나 적어도 부분적으로?

또한 객체가 범위를 벗어나면 가비지 수집기의 다음 단계를 위해 수집 준비가 완료된 것으로 표시됩니다 (시간이 걸릴 수 있음).

따라서 이것을 염두에두고 null메모리가 더 이상 범위에 있지 않으며 부작용이 없다는 것을 해결할 필요가 없으므로 메모리를 해제하는 시스템 속도를 높이도록 설정합니다 .

MSDN 기사는 예제 에서이 작업을 수행하지 않으며 현재 해를 볼 수 없으므로이 작업을 수행합니다. 그러나 나는 의견이 혼합되어 의견이 유용합니다.


4
+1 좋은 질문입니다. 컴파일러가 할당을 완전히 최적화하는 상황을 아는 사람이 있습니까? 즉, 다른 환경에서 MSIL을 조사한 사람에게 개체를 null로 설정 (또는 부족) 한 IL에 주목했습니다.
Tim Medora

답변:


73

Karl은 절대적으로 정확합니다. 사용 후 객체를 null로 설정할 필요가 없습니다. 객체가을 구현하는 경우 해당 객체 ( .. 또는 블록에 싸여 있음)를 완료 IDisposable하면 호출해야합니다 . 그러나을 호출하는 것을 기억하지 않아도 객체의 finaliser 메소드가 호출해야 합니다.IDisposable.Dispose()tryfinallyusing()Dispose()Dispose()

나는 이것이 좋은 치료법이라고 생각했다.

IDisposable에 파기

IDisposable 이해

자체 조정 및 불투명하기 때문에 GC 및 관리 전략을 다시 추측하려는 데는 아무런 의미가 없습니다. 닷 넷에 제프리 리히터와 내부 동작에 대한 좋은 토론이 있었다 여기 록스 : 제프리 Windows 메모리 모델에 리히터 와 Richters 예약 C #을 통해 CLR을 20 장 좋은 치료가 있습니다 :


6
null로 설정하지 않는 규칙은 "단단하고 빠르지 않습니다"... 객체가 큰 객체 힙에 놓이면 (크기가> 85K) 완료되면 객체를 null로 설정하면 GC에 도움이됩니다. 그것을 사용합니다.
Scott Dorman

나는 어느 정도 동의하지만 메모리 부족을 경험하지 않으면 사용 후 객체를 null로 설정하여 '조기 최적화'할 필요가 없습니다.
Kev

21
"조기 최적화하지 마십시오"라는이 사업 전체는 "느리게 선호하고 CPU 속도가 빨라지고 CRUD 앱에는 속도가 필요하지 않으므로 걱정하지 않아도됩니다." 그래도 나일지도 모른다. :)
BobbyShaftoe

19
이것이 의미하는 바는 "가비지 콜렉터가 사용자보다 메모리 관리에 더 좋습니다"입니다. 그래도 나일지도 모른다. :)
BobRodes 2016 년

2
@BobbyShaftoe : "조기 최적화가 나쁘다"라고 말하는 것은 아마도 잘못된 것입니다. 합리적인 프로그래머도 말할 수 없습니다. 뉘앙스와 최적화에 대한 현명한 문제입니다. 개인적으로 코드 명료성 및 실제 테스트 성능에 대해 걱정할 것입니다. 개인적으로 많은 사람들 (어렸을 때 자신을 포함하여)이 "완벽한"알고리즘을 만드는 데 너무 많은 시간을 소비하여 0.1ms를 절약 할 수있었습니다. 가독성이 완전히 반영된 상태에서 100,000 회 반복
브렌트 리튼 하우스

36

작업을 완료 할 때 개체를 null로 설정하지 않는 또 다른 이유는 실제로 개체를 더 오래 살릴 수 있기 때문입니다.

예 :

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is now eligible for garbage collection         

    // ... rest of method not using 'someType' ...
}

"DoSomething"을 호출 한 후 someType에 의해 참조 된 객체를 GC 할 수 있지만

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is NOT eligible for garbage collection yet
    // because that variable is used at the end of the method         

    // ... rest of method not using 'someType' ...
    someType = null;
}

때로는 메소드가 끝날 때까지 객체를 활성 상태로 유지할 수 있습니다. JIT 보통 NULL로 지정을 얻어 최적화 된 것 같은 코드 인 것을 단부의 두 비트 있도록.


흥미로운 점입니다. 나는 항상 범위가 지정된 방법이 완료 될 때까지 객체가 범위를 벗어나지 않는다고 생각했습니다. 물론 개체가 Using 블록 내에서 범위가 지정되거나 명시 적으로 Nothing 또는 null로 설정되어 있지 않은 한.
Guru Josh

1
그들이 살아있게하는 가장 좋은 방법 GC.KeepAlive(someType);ericlippert.com/2013/06/10/construction-destruction
NotMe

14

객체를 null하지 않습니다. 당신은 체크 아웃 할 수 http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx을 자세한 정보는하지만, 사물을 null로 설정하면 더러운 코드를 제외하고는 아무것도하지 않습니다.


1
공유 링크의 메모리에 대한
훌륭하고

링크가 끊어졌습니다. 링크 된 내용이 없으면이 답변은 쓸모가 없으므로 삭제해야합니다.
Imre Pühvel

7

또한:

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

7

일반적으로 사용 후 객체를 null로 지정할 필요는 없지만 어떤 경우에는 좋은 방법입니다.

객체가 IDisposable을 구현하고 필드에 저장되면 배치 된 객체를 사용하지 않기 위해 null로 만드는 것이 좋습니다. 다음과 같은 종류의 버그는 고통 스러울 수 있습니다.

this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();

필드를 삭제 한 후 필드를 null로 설정하고 필드를 다시 사용하는 줄에서 NullPtrEx를 가져 오는 것이 좋습니다. 그렇지 않으면 DoSomething의 기능에 따라 약간의 버그가 발생할 수 있습니다.


8
폐기 된 객체는 이미 폐기 된 경우 ObjectDisposedException을 발생시켜야합니다. 이것은 내가 아는 한 모든 곳에서 상용구 코드가 필요하지만 Disposed는 어쨌든 신중하게 생각되는 패러다임입니다.
nicodemus13

3
대한 Ctrl 키 + F .Dispose(). 찾은 경우 IDisposable을 올바르게 사용하지 않는 것입니다. 일회용 물체의 유일한 사용은 using-block의 경계에 있어야합니다. 그리고 사용 차단 후에는 myField더 이상 액세스 할 수 없습니다. 그리고 using 블록 내에서 설정 null이 필요하지 않은 경우 using-block은 객체를 처리합니다.
Suamere

7

null변수 가 필요하다고 느끼면 코드가 제대로 구성되지 않았을 가능성이 있습니다 .

변수의 범위를 제한하는 방법에는 여러 가지가 있습니다.

Steve Tranby가 언급했듯이

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

마찬가지로 중괄호를 사용하면됩니다.

{
    // Declare the variable and use it
    SomeObject object = new SomeObject()
}
// The variable is no longer available

"제목"없이 중괄호를 사용하면 코드를 실제로 정리하고 더 이해하기 쉽게 만들 수 있습니다.


사용자 지정 로컬 범위를 한 번 사용하려고했습니다 (주로 smarta $ $). 회사는 폭발했다.
Suamere

또 다른 참고 사항 : c # 컴파일러는 IDisposable을 구현하는 로컬 범위 변수를 찾고 범위가 끝나면 .Dispose (MOST Of the time)를 호출하기 때문입니다. 그러나 .Dispose ()가 최적화되지 않은 경우 SQL 연결은 큰 시간입니다. 명백한주의가 필요한 일부 유형이 있으므로 개인적으로 항상 명시 적으로 일을하므로 물지 않습니다.
Suamere

5

변수를 널로 설정해야하는 유일한 경우는 변수가 범위를 벗어나지 않고 더 이상 연관된 데이터가 필요하지 않은 경우입니다. 그렇지 않으면 필요가 없습니다.


2
사실이지만 코드를 리팩토링해야 할 수도 있습니다. 의도 된 범위 밖에서 변수를 선언해야한다고 생각하지 않습니다.
Karl Seguin

2
"variable"이 객체 필드를 포함하는 것으로 이해되면이 대답은 의미가 있습니다. "variable"이 (메소드의 "local variable"만 의미하는 경우) 여기서 틈새 사례에 대해 이야기하고있을 것입니다 (예 : 평소보다 훨씬 더 긴 시간 동안 실행되는 메소드).
stakx-더 이상

5

일반적으로 null로 설정할 필요가 없습니다. 그러나 클래스에 재설정 기능이 있다고 가정하십시오.

그런 다음 Dispose 중 일부가 올바르게 구현되지 않고 System.ObjectDisposed 예외가 발생할 수 있으므로 dispose를 두 번 호출하지 않으려 고 할 수 있습니다.

private void Reset()
{
    if(_dataset != null)
    {
       _dataset.Dispose();
       _dataset = null;
    }
    //..More such member variables like oracle connection etc. _oraConnection
 }

별도의 플래그로 이것을 추적하는 것이 가장 좋습니다.
Thulani Chivandikwa

3

이런 종류의 "사용 후 객체를 null로 설정할 필요가 없다"는 완전히 정확한 것은 아닙니다. 변수를 폐기 한 후 NULL로 설정해야하는 경우가 있습니다.

그렇습니다, 당신은 항상 전화 .Dispose()또는 .Close()당신이 끝났을 때 그것을 가지고 무엇이든해야합니다. 파일 핸들, 데이터베이스 연결 또는 일회용 개체 여야합니다.

그것과는 별도로 LazyLoad의 매우 실용적인 패턴입니다.

내가 가지고있는 인스턴스화 말 ObjAclass A. Class A의 공개 자산 PropBclass B있습니다.

내부적으로 PropB의 개인 변수를 사용 _B하며 기본값은 null입니다. 경우 PropB.Get()사용되며, 그 검사가 있는지 확인하기 위해 _PropB널 (null)이고 그것이 경우 인스턴스화 필요한 자원 열리고 B으로이 _PropB. 그런 다음를 반환합니다 _PropB.

내 경험에 따르면 이것은 정말 유용한 트릭입니다.

널이 필요한 곳은 A의 내용을 _PropB이전 값의 하위 항목 으로 변경 한 경우 A를 재설정하거나 변경하는 경우, 코드가있는 경우 LazyLoad가 재설정하여 올바른 값을 가져 오도록 처리하고 A널 아웃 _PropB해야합니다. 필요합니다.

만 않으면 _PropB.Dispose()직후 성공 LazyLoad에 대한 널 체크를 기대, 그것은 null이되지 않습니다, 당신은 오래된 데이터를 찾고있을거야. 실제로, Dispose()확실하게하기 위해 그것을 null 로해야합니다.

나는 확실히 그것을 달리했다 좋겠지 만, 나는 지금 한 후이 동작을 보이는 코드를 가지고있어 Dispose()A의 _PropB는 폐기 한 호출 기능의 외부 (따라서 거의 밖으로 범위), 개인 소품이 여전히 null가 아닌 오래된 데이터는 여전히 존재합니다.

결국 처분 된 속성은 무효화되지만 그것은 내 관점에서 결정적이지 않습니다.

dbkk가 주장하는 핵심 이유는 부모 컨테이너 ( ObjAwith PropB)가 인스턴스 _PropB에도 불구하고 범위를 유지하기 때문 Dispose()입니다.


수동으로 null을 설정하는 방법을 보여주는 좋은 예는 호출자에게 더 치명적인 오류를 의미합니다.
굴림

1

널 참조에 적합한 경우가 있습니다. 예를 들어 우선 순위 대기열과 같은 컬렉션을 작성하고 계약에 따라 클라이언트가 대기열에서 객체를 제거한 후에도 해당 객체를 클라이언트에 대해 활성 상태로 유지해서는 안됩니다.

그러나 이런 종류의 물건은 오래 살았던 컬렉션에서만 중요합니다. 큐가 생성 된 함수의 끝에서 큐가 살아남지 않으면 훨씬 덜 중요합니다.

전체적으로 귀찮게해서는 안됩니다. 컴파일러와 GC가 작업을 수행 할 수 있도록하십시오.



1

Stephen Cleary는이 게시물에서 잘 설명합니다. 가비지 수집을 지원하려면 변수를 Null로 설정해야합니까?

말한다 :

변수가 정적 필드이거나 열거 가능한 메소드 (수율 리턴 사용) 또는 비동기 메소드 (async 및 await 사용)를 작성중인 경우 조바심 예에 대한 짧은 답변. 그렇지 않으면 아닙니다.

즉, 열거 할 수없고 비동기가 아닌 일반 메소드에서는 로컬 변수, 메소드 매개 변수 또는 인스턴스 필드를 널로 설정하지 않습니다.

(IDisposable.Dispose를 구현하는 경우에도 변수를 null로 설정해서는 안됩니다).

우리가 고려해야 할 중요한 것은 정적 필드 입니다.

정적 필드는 항상 루트 객체 이므로 가비지 수집기에서 항상 "사용 중" 으로 간주됩니다 . 정적 필드가 더 이상 필요하지 않은 객체를 참조하는 경우 가비지 수집기가 해당 객체를 수집 할 수있는 것으로 간주하도록 null로 설정해야합니다.

전체 프로세스가 종료되는 경우 정적 필드를 널로 설정하는 것은 의미가 없습니다. 모든 힙 개체를 포함하여 해당 시점에서 전체 힙이 가비지 수집됩니다.

결론:

정적 필드 ; 그게 다야 다른 것은 시간 낭비입니다 .


0

GC 구현 자의 설계에 따르면 무효화로 GC 속도를 높일 수는 없습니다 . 나는 그들이 어떻게 / GC가 실행될 때 자신을 걱정하지 선호하는 거라고 확신 해요 - 유비쿼터스와 같은 대접을 존재가 당신을 위해 밖으로 ... (활 머리를 아래로, 하늘에 주먹을 제기) 보호 이상보고 .. .

개인적으로, 나는 종종 자체 문서화의 형태로 변수를 완성했을 때 변수를 명시 적으로 null로 설정합니다. 선언하고 사용하지 않고 나중에 null로 설정하지 않습니다. 더 이상 필요하지 않으면 즉시 null입니다. 나는 "공식적으로 당신과 함께하고 ... 사라 졌어 ..."

GC 언어로 무효화해야합니까? 아니요. GC에 도움이 되나요? 어쩌면 예, 아마도 아니오, 확실하지 않습니다. 설계 상 실제로 제어 할 수 없으며이 버전에 대한 오늘의 답변이나 미래의 GC 구현에 관계없이 미래의 GC 구현은 내 통제 범위를 벗어나 답변을 변경할 수 있습니다. 또한 널링이 최적화 된 경우에는 멋진 의견에 지나지 않습니다.

나는 발자취를 따르는 다음 가난한 바보에게 내 의도를 더 명확하게 만들고 그것이 때때로 GC에 도움 이 될 수 있다면 나에게 가치가 있다고 생각합니다. 대부분 깔끔하고 깨끗하게 느끼고 몽고는 깔끔하고 깨끗하게 느끼기를 좋아합니다. :)

나는 이것을 다음과 같이 본다 : 프로그래밍 언어는 사람들이 다른 사람들에게 의도에 대한 아이디어와 컴파일러에게 무엇을해야하는지에 대한 작업 요청을 주도록한다-컴파일러는 그 요청을 CPU에 대해 다른 언어로 (때로는 여러 언어로) 변환한다- CPU는 사용하는 언어, 탭 설정, 주석, 문체 강조, 변수 이름 등을 알려줄 수 있습니다. CPU에는 비트 스트림에 관한 모든 것이 있으며 레지스터 및 opcode 및 트위스트 할 메모리 위치를 알려줍니다. 코드로 작성된 많은 것들이 우리가 지정한 순서대로 CPU가 소비하는 것으로 변환되지 않습니다. 우리의 C, C ++, C #, Lisp, Babel, 어셈블러 또는 실제보다는 이론이 무엇이든, 작업 진술로 작성되었습니다. 당신이 보는 것은 그렇습니다. 어셈블러 언어조차도 아닙니다.

"빈 줄과 같은"필요하지 않은 것 "의 사고 방식은"잡음과 혼란스러운 코드 일뿐 "입니다. 저의 경력 초기에 저였습니다. 나는 그것을 완전히 얻는다. 이 시점에서 나는 코드를 더 명확하게 만드는쪽으로 기울었다. 프로그램에 50 줄의 "노이즈"를 추가하는 것과는 다릅니다. 여기 저기 몇 줄 있습니다.

규칙에는 예외가 있습니다. 휘발성 메모리, 정적 메모리, 경쟁 조건, 싱글 톤, "스톨 (stale)"데이터 사용 및 모든 종류의 썩음이 발생하는 시나리오는 다릅니다. 메모리는 자신의 메모리에 속하지 않기 때문에 자신의 메모리를 관리하고, 제안으로 잠금 및 무효화해야합니다. GC'd 우주-잘만되면 모든 사람들이 그것을 이해합니다. GC 언어로 된 나머지 시간은 필요하거나 보장 된 성능 향상보다는 스타일의 문제입니다.

하루가 끝나면 GC에 적합한 대상과 그렇지 않은 대상을 이해해야합니다. 적절하게 잠금, 폐기 및 무효화; 왁스 온, 왁스 오프; 숨을들이 쉬고 내쉬세요; 그리고 내가 말하는 다른 모든 것에 대해 : 기분이 좋으면 그렇게하십시오. 마일리지는 다를 수 있습니다 ...


0

무언가를 null로 되 돌리는 것이 지저분하다고 생각합니다. 이제 설정되어있는 항목이 속성을 통해 노출되는 시나리오를 상상해보십시오. 이제 어떻게 든 코드 조각이 항목을 폐기 한 후 실수 로이 속성을 사용하므로 null 참조 예외가 발생하여 정확히 무슨 일이 일어나고 있는지 파악해야합니다.

프레임 워크 일회용이 더 의미있는 ObjectDisposedException을 던질 수 있다고 생각합니다. 이러한 이유로 null을 다시 설정하지 않는 것이 좋습니다.


-1

어떤 객체 .dispose()는 리소스를 메모리에서 강제로 제거 하는 방법을 가정합니다 .


11
아닙니다. Dispose ()는 개체를 수집 하지 않습니다. 결정적 정리를 수행하는 데 사용되며 일반적으로 관리되지 않는 리소스를 해제합니다.
Marc Gravell

1
결정론은 관리되지 않는 리소스 (예 : 메모리)가 아니라 관리되는 리소스에만 적용된다는 것을 명심하십시오.
nicodemus13
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.