[C#] 객체를 폐기하고 null로 설정해야합니까?

객체를 폐기하고 null로 설정해야합니까, 아니면 가비지 수집기가 객체가 범위를 벗어날 때 정리합니까?



답변

더 이상 사용되지 않거나 가비지 수집기가 적합 할 때 개체가 정리됩니다. 때로는 null범위를 벗어나기 위해 객체를 설정해야 할 수도 있지만 (예 : 더 이상 필요없는 정적 필드) 일반적으로로 설정할 필요가 없습니다 null.

객체 폐기와 관련하여 @Andre에 동의합니다. 객체 인 경우 IDisposable는이다 그것을 처리 할 수있는 좋은 아이디어 가 더 이상 객체가 관리되지 않는 리소스를 사용하는 경우 특히 필요없는 경우는. 관리되지 않는 리소스를 폐기하지 않으면 메모리 누수가 발생 합니다.

using프로그램이 using명령문 범위를 벗어나 면 명령문을 사용하여 오브젝트를 자동으로 폐기 할 수 있습니다 .

using (MyIDisposableObject obj = new MyIDisposableObject())
{
    // use the object here
} // the object is disposed here

기능적으로 다음과 같습니다.

MyIDisposableObject obj;
try
{
    obj = new MyIDisposableObject();
}
finally
{
    if (obj != null)
    {
        ((IDisposable)obj).Dispose();
    }
}


답변

C ++에서와 마찬가지로 개체는 C #에서 범위를 벗어나지 않습니다. 더 이상 사용하지 않으면 가비지 콜렉터가 자동으로 처리합니다. 이것은 변수의 범위가 완전히 결정적인 C ++보다 더 복잡한 접근법입니다. CLR 가비지 콜렉터는 작성된 모든 오브젝트를 적극적으로 통과하며 사용중인 경우이를 해결합니다.

객체는 한 함수에서 “범위를 벗어난”상태가 될 수 있지만 값이 반환되면 GC는 호출 함수가 반환 값을 유지하는지 여부를 확인합니다.

null가비지 수집은 다른 객체가 참조하는 객체를 계산하여 가비지 수집이 작동하므로 객체 참조를 설정 하지 않아도됩니다.

실제로, 당신은 파괴에 대해 걱정할 필요가 없습니다, 그것은 효과가 있으며 훌륭합니다 🙂

DisposeIDisposable작업을 마쳤을 때 구현하는 모든 개체에 대해 호출해야 합니다. 일반적으로 다음 using과 같은 객체에 블록을 사용합니다 .

using (var ms = new MemoryStream()) {
  //...
}

편집 변수 범위에서. Craig는 변수 범위가 객체 수명에 어떤 영향을 미치는지 물었습니다. CLR의 이러한 측면을 올바르게 설명하려면 C ++ 및 C #의 몇 가지 개념을 설명해야합니다.

실제 변수 범위

두 언어 모두 변수는 클래스, 함수 또는 중괄호로 묶인 명령문 블록과 같이 정의 된 것과 동일한 범위에서만 사용할 수 있습니다. 그러나 미묘한 차이점은 C #에서는 중첩 블록에서 변수를 다시 정의 할 수 없다는 것입니다.

C ++에서 이것은 완벽하게 합법적입니다.

int iVal = 8;
//iVal == 8
if (iVal == 8){
    int iVal = 5;
    //iVal == 5
}
//iVal == 8

그러나 C #에서는 컴파일러 오류가 발생합니다.

int iVal = 8;
if(iVal == 8) {
    int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}

생성 된 MSIL을 보면 함수에 사용되는 모든 변수가 함수 시작시 정의됩니다. 이 기능을 살펴보십시오.

public static void Scope() {
    int iVal = 8;
    if(iVal == 8) {
        int iVal2 = 5;
    }
}

아래는 생성 된 IL입니다. if 블록 내에 정의 된 iVal2는 실제로 함수 레벨에서 정의됩니다. 실제로 이것은 C #에 가변 수명에 관한 한 클래스 및 함수 수준 범위 만 있음을 의미합니다.

.method public hidebysig static void  Scope() cil managed
{
  // Code size       19 (0x13)
  .maxstack  2
  .locals init ([0] int32 iVal,
           [1] int32 iVal2,
           [2] bool CS$4$0000)

//Function IL - omitted
} // end of method Test2::Scope

C ++ 범위 및 객체 수명

스택에 할당 된 C ++ 변수는 범위를 벗어날 때마다 소멸됩니다. C ++에서는 스택이나 힙에 객체를 만들 수 있습니다. 스택에서 생성하면 실행이 범위를 벗어나면 스택에서 튀어 나와 파괴됩니다.

if (true) {
  MyClass stackObj; //created on the stack
  MyClass heapObj = new MyClass(); //created on the heap
  obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives

C ++ 오브젝트가 힙에 작성되면 명시 적으로 파기해야합니다. 그렇지 않으면 메모리 누수입니다. 스택 변수에 대해서는 그런 문제가 없습니다.

C # 개체 수명

CLR에서 객체 (예 : 참조 유형)는 항상 관리되는 힙에 생성됩니다. 이것은 객체 생성 구문에 의해 더욱 강화됩니다. 이 코드 스 니펫을 고려하십시오.

MyClass stackObj;

C ++에서는 MyClass스택에 인스턴스를 만들고 기본 생성자를 호출합니다. C #에서는 MyClass아무것도 가리 키지 않는 클래스에 대한 참조를 만듭니다 . 클래스의 인스턴스를 만드는 유일한 방법은 new연산자 를 사용하는 것 입니다.

MyClass stackObj = new MyClass();

어떤 식 으로든 C # 객체는 newC ++에서 구문을 사용하여 생성 된 객체와 매우 유사합니다. 이들은 힙에서 생성되지만 C ++ 객체와 달리 런타임에 의해 관리되므로 파괴에 대해 걱정할 필요가 없습니다.

객체가 항상 힙에 있기 때문에 객체 참조 (예 : 포인터)가 범위를 벗어난다는 사실이 무질서하게됩니다. 단순히 객체에 대한 참조가 존재하는 것보다 객체를 수집할지 여부를 결정하는 데 더 많은 요소가 있습니다.

C # 개체 참조

Jon Skeet 은 Java의 객체 참조를 풍선에 연결된 문자열 조각 ( 객체)과 비교했습니다 . C # 객체 참조에도 동일한 비유가 적용됩니다. 단순히 객체를 포함하는 힙의 위치를 ​​가리 킵니다. 따라서 null로 설정하면 객체 수명에 즉각적인 영향을 미치지 않으며 풍선이 GC가 “팝핑”할 때까지 계속 존재합니다.

풍선 비유를 계속하면 풍선에 부착 된 끈이 ​​없으면 파괴 될 수 있다는 것이 논리적으로 보입니다. 실제로 이것은 참조 계산 된 개체가 관리되지 않는 언어에서 작동하는 방식과 정확히 동일합니다. 이 방법을 제외하고 순환 참조에는 잘 작동하지 않습니다. 두 개의 풍선이 끈으로 연결되어 있지만 풍선에 다른 끈이 없습니다. 간단한 심판 계산 규칙에 따라 전체 풍선 그룹이 “분리 된”경우에도 둘 다 계속 존재합니다.

.NET 객체는 지붕 아래의 헬륨 풍선과 매우 유사합니다. 지붕이 열리면 (GC가 실행 됨) 서로 묶여있는 풍선 그룹이있을 수 있지만 사용하지 않는 풍선이 뜹니다.

.NET GC는 세대 별 GC와 마크 앤 스윕의 조합을 사용합니다. 세대 별 접근 방식은 가장 최근에 할당 된 객체를 검사하는 것을 선호하는 런타임과 관련이 있습니다. 사용하지 않을 가능성이 높고 마크 및 스윕은 런타임에 전체 객체 그래프를 살펴보고 사용하지 않는 객체 그룹이있는 경우 작동합니다. 이것은 순환 종속성 문제를 적절하게 처리합니다.

또한 .NET GC는 다른 스레드 (파이널 라이저 스레드라고도 함)에서 실행되며 꽤 많은 작업을 수행하므로 기본 스레드에서 수행하면 프로그램이 중단됩니다.


답변

다른 사람들이 말했듯이 Dispose클래스가 구현 하면 분명히 전화하고 싶습니다 IDisposable. 나는 이것에 대해 상당히 엄격한 입장을 취합니다. 일부 세력의 주장은 호출하는 것을 DisposeDataSet그들은 그것을 분해하고 의미있는 아무것도하지 않았다는 것을 보았 기 때문에, 예를 들어, 무의미하다. 그러나 나는 그 주장에 오류가 많다고 생각합니다.

주제에 대해 존경받는 사람들의 흥미로운 토론에 대해 읽으십시오 . 그런 다음 Jeffery Richter가 잘못된 캠프에 있다고 생각하는 이유를 여기에서 읽으십시오 .

이제 참조를로 설정해야하는지 여부를 결정합니다 null. 내 대답은 아니오 야. 다음 코드로 요점을 설명하겠습니다.

public static void Main()
{
  Object a = new Object();
  Console.WriteLine("object created");
  DoSomething(a);
  Console.WriteLine("object used");
  a = null;
  Console.WriteLine("reference set to null");
}

그렇다면 언제 참조하는 객체 a를 수집 할 수 있다고 생각 하십니까? 당신이 전화 후 말했다면 당신 a = null은 잘못입니다. Main방법이 완료된 후에 말한 경우 에도 잘못된 것입니다. 정답은 에 전화하는 동안 언젠가 수금 자격이 있다는 것입니다 DoSomething. 맞아 그거야. 그것은 자격이 되기 전에 참조로 설정 null도 호출하기 전에 아마 및 DoSomething완료. JIT 컴파일러는 객체 참조가 아직 루팅되어 있어도 더 이상 참조 해제되지 않는 경우를 인식 할 수 있기 때문입니다.


답변

C #에서 개체를 null로 설정할 필요는 없습니다. 컴파일러와 런타임은 더 이상 범위에 속하지 않을 때 알아서 처리합니다.

예, IDisposable을 구현하는 객체를 폐기해야합니다.


답변

나는 일반적인 대답에 동의합니다. 그렇습니다. 처분해야하며 일반적으로 변수를 null로 설정해서는 안됩니다 …하지만 처분은 주로 메모리 관리에 관한 것이 아니라고 지적하고 싶습니다. 그렇습니다. 메모리 관리에 도움이 될 수도 있고 때로는 도움이 될 수도 있지만, 주된 목적은 부족한 리소스를 결정적으로 해제하는 것입니다.

예를 들어, 하드웨어 포트 (예 : 직렬), TCP / IP 소켓, 파일 (독점 액세스 모드) 또는 데이터베이스 연결을 열면 다른 코드가 해제 될 때까지 해당 항목을 사용하지 못하게되었습니다. Dispose는 일반적으로 이러한 항목을 릴리스합니다 (GDI 및 기타 “os”핸들과 함께 1000 개가 있지만 여전히 전체적으로 제한됨). 소유자 개체에 대해 dipose를 호출하지 않고 이러한 리소스를 명시 적으로 해제 한 경우 나중에 처리되지 않은 수집되지 않은 개체에 여전히 항목이 열려 있기 때문에 열기 시도가 실패하는 동일한 리소스를 다시 열거 나 다른 프로그램에서 다시 시도하십시오. . 물론, GC가 아이템을 수집 할 때 (Dispose 패턴이 올바르게 구현 된 경우) 리소스가 해제 될 것입니다.하지만 그 시점이 언제인지 알지 못하므로 해당 리소스를 다시 열어도 안전한지 알 수 없습니다. 이것은 Dispose가 해결하는 주요 문제입니다. 물론, 이러한 핸들을 해제하면 종종 메모리도 해제되고, 해제하지 않으면 해당 메모리가 해제되지 않을 수 있습니다. 따라서 메모리 누수 또는 메모리 정리 지연에 대한 모든 이야기가 있습니다.

나는 이것이 문제를 일으키는 실제 사례를 보았습니다. 예를 들어, SQL 서버의 ‘연결 풀이 가득 찼기’때문에 ASP.Net 웹 응용 프로그램이 결국 데이터베이스에 연결하지 못하는 경우를 보았습니다 (짧은 시간이나 웹 서버 프로세스가 다시 시작될 때까지). 너무 많은 연결이 생성되어 너무 짧은 시간 내에 명시 적으로 해제되지 않아서 새로운 연결을 만들 수 없으며 풀의 많은 연결이 활성 상태는 아니지만 여전히 미 확산 및 수집되지 않은 개체에 의해 참조되므로 재사용하지 마십시오. (당신이하지 않으면되지 적어도 올바르게 필요한 보장하지만이 문제가 발생하지 않습니다 데이터베이스 연결을 폐기 매우 높은 동시 액세스).


답변

객체가를 구현 IDisposable하면 예, 처리해야합니다. 개체가 즉시 해제되지 않을 수있는 기본 리소스 (파일 핸들, OS 개체)에 걸려있을 수 있습니다. 이로 인해 리소스 부족, 파일 잠금 문제 및 피할 수있는 기타 미묘한 버그가 발생할 수 있습니다.

MSDN 에서 Dispose Method 구현을 참조하십시오 .


답변

이들이 IDisposable 인터페이스를 구현하는 경우이를 폐기해야합니다. 가비지 수집기가 나머지를 처리합니다.

편집 :using 일회용 항목으로 작업 할 때 명령 을 사용하는 것이 가장 좋습니다 .

using(var con = new SqlConnection("..")){ ...