2012-01-12 1 views
2

내가 어떤 스트림에 쓸 때 나는 비트 맵MemoryStream에 .Save() 비트 맵을 .Dispose()하지 않으면 메모리 누수가 발생하는 이유는 무엇입니까? (HttpListenerResponse에 의해 주어진 내 경우에는, 그것은 HttpResponseStream이야)

Bitmap bitmap = new Bitmap(320, 200); 

을 만들 말, 다 괜찮 :

bitmap.Save(stream, ImageFormat.Png); 

I 돈 bitmap.Dispose()가 필요합니다. 비트 맵에서 사용하는 리소스가 자동으로 정리됩니다. 그러나 PNG를 직접 검색 할 수없는 스트림에 쓰는 문제는 A generic error occurred in GDI+이 될 수 있다는 것입니다. Azure에서 내 Asp 앱을 사용해 보았을 때 그 일이 일어났습니다. 그래서 내 코드는 다음과 같습니다.

using (MemoryStream ms = new MemoryStream()) 
{ 
    bitmap.Save(ms, ImageFormat.Png); 
    ms.WriteTo(stream); 
} 

이제 bitmap.Dispose()를 실행하지 않으면 누출됩니다.

고쳐 질문보다 구체적인 답변을 얻을 수 있습니다 : 왜 비트 맵이 메모리 누수가 나는에 저장 스트림의 유형에 따라이 나타 납니까?

업데이트 : 나는 그것이 누출 확신 있다면 댓글에 물어 봤는데으로 . 위의 내용을 스트레스 테스트에서 반복적으로 호출하면 내 컴퓨터가 스왑을 시작할 때까지 내 w3wp 프로세스가 사용 된 메모리와 기가비트까지 올라가며 정리되지 않습니다.

+0

메모리 누수가 확실하고 가비지 수집이 아직 정리되지 않았습니까? – Matthew

+0

메모리 누수가 발생 했습니까? 그들은 .Net의 발명으로 메모리 누출의 정의를 재정의했습니다.) – James

+0

@James; GDI +를 사용하면 AppDomain이 해제 된 후에 정리되는 시스템 메모리 누수가 발생할 수 있습니다. – AMissico

답변

5

비트 맵 클래스는 관리되지 않는 리소스를 사용합니다. 이러한 리소스는 메모리 스트림 클래스에서 사용하는 리소스와 관련이 없습니다. using 문에서 비트 맵 클래스를 래핑하면 완료 한 비트 맵 인스턴스를 처리 할 수 ​​있습니다.

질문의 후반 부분을 놓치 셨습니다. "설정하고 잊어 버리는"한 가지 방법은 비트 맵 인스턴스를 노출하지만 비트 맵 인스턴스를 삭제하는 소멸자를 구현하는 래퍼 클래스를 만드는 것입니다. 이 소멸자는 비트 맵 클래스가 가비지 수집에서 암시 적으로 처리된다는 것을 의미합니다.

최종 참고 사항 : IDisposable을 구현하는 모든 인스턴스 생성 객체는 코드에서 처리해야합니다. Dipose는 절대로 암시 적으로 호출되지 않습니다. 귀하의 첫 번째 예에서도. 데이터를 스트림에 저장했기 때문에 메모리가 할당 해제 된 것만은 아닙니다. 대부분의 경우 객체를 인스턴스화 한 동일한 코드 세그먼트 내에 객체를 배치하는 것이 좋습니다. 이렇게하면 코드 투명성이 향상되어 코드를 더 쉽게 읽을 수 있습니다.

+1

Bitmap.Dispose()를 호출하지 않고 비트 맵을 가져 오거나 정리하지 않습니다. 스트림의 종류 (.)에만 의존하여 .Isave()를 호출합니다. 그게 나에게 당황 스럽다. 그리고 btw, Bitmap.Dispose()는 실제로 암시 적으로 호출됩니다 ~ Image() 소멸자가 그것을 않습니다. 소스 코드를 확인하십시오. –

0

GDI + 리소스를 해제하려면 비트 맵을 삭제해야합니다. 그렇게 간단합니다. Dispose가 요구되는 몇 안되는시기 중 하나입니다. 디스크 액세스를 줄이기 위해 비트 맵을 캐시하는 경우 이미지를 복제하고 복제본을 사용하여 스트림에 저장하십시오. 스트림을 플러시, 닫기 및 처분하는 것이 좋습니다. 완료되면 clone 및 stream 변수를 null로 설정하십시오.

+0

MemoryStream이 아닌 다른 곳으로 저장할 때 누출이없는 이유는 무엇입니까? 나는 Dispose를 거기에서 부르지 않을 것이고, 잘 동작한다. Bitmap.Dispose()는 실제로 암시 적으로 호출되지만 ~ Image() 소멸자는이를 수행합니다. –

+0

복제에서 어떤 이점이 있습니까? 나는 복제가 무엇인지 모르지만 더 많은 기억을 소비하는 것처럼 들린다. (MSDN 문서 상태 :이 이미지의 정확한 복사본을 만듭니다.). Btw., 디스크가 내 사건에 관여하지 않습니다. 이들은 메모리에 대해 CPU 시간을 절약하기 위해 캐시 된 생성 된 이미지입니다. –

+0

@EugeneBeresovksy; 예, Dispose가 암시 적으로 호출됨을 알고 있습니다. .NET이 GDI 라이브러리를 초기화하고 종료한다는 것도 알고 있습니다. 내가 말한 것은 10 년의 .NET 경험을 기반으로하고 Dispose를 호출해야하며 이미지를 보유하는 변수를 null로 설정해야합니다. – AMissico

1

GC가 마술처럼 개체를 정리할 것이라고 가정합니다. 그러나 결코 그렇게 할 수는 없으며 다음과 같이 생각할 수 있습니다.

비트 맵은 비트 맵 데이터를 보유하기 위해 관리되지 않는 리소스를 사용하며 비트 맵 데이터가 큽니다. 따라서 작은 블럭의 관리 메모리와 각 비트 맵에 대한 관리되지 않는 메모리 블록을 할당하게됩니다.

그래서 여가 시간에 수집 할 수 있도록 비트 맵을 놓아 둡니다.GC가 메모리를 다시 사용하기 위해 수집하는 메모리 압박이 곧 있기 때문에 많은 객체에서 잘 작동합니다. GC는 관리되는 힙을보고 "uniused 객체를 삭제하면 64 바이트의 메모리 만 복구 할 수 있습니다."라고 말합니다. 기가 바이트의 관리되지 않는 리소스는 볼 수 없으며 힙의 몇 바이트 만 보입니다.

그래서 비트 맵을 추적하고 처리해야합니다.

가끔 당신이 그것을 깨끗하게 보았을 가능성이 있습니다. 이는 메모리 공간이 큰 스트림과 같은 다른 객체를 처분 할 때나 화요일 오후 일 때와 같이 sme 상황에서 사용하기 때문에 을 사용하여 사용되지 않는 메모리 블록을 처리하도록 선택하면 비트 맵이 처리됩니다. 마침내. 그러나 당신은 이 일에 의존 수 없습니다.

... 산책 :

옛날에 포인터 두 가지 문제가 있었다.

  • 그들은 당신은 자신의 메모리/리소스를 해제하고 그들이 "포인터"를 "참조"이름이 그래서 .NET에서 누출

을 얻을 것을 잊지 수있는 코드

  • 충돌로 이어지는, null이 될 수있다, GC 및 을 추가하여 더 이상 문제가 존재하지 않는다는 메시지가 인 것으로 나타났습니다. 참조가 여전히 null 일 수 있고 프로그래머가 누출을 피하기 위해 리소스를 추적하고 관리해야한다는 점을 제외하고는 조금만 더 자주합니다. 나는 이것이 나쁜 것이라고 생각한다. 그것은 근본적인 문제를 실제로 제거하지 않고 우리를 게으르고 비효율적으로 만든다. 그러면 우리에게 물러서서 우리에게 물어 뜯는다. 그리고 우리는 단순한 '삭제' 우리의 파괴자들.