2011-01-17 3 views
1

2 개의 유사한 응용 프로그램이 있습니다. 하나는 관리되고 다른 하나는 해제됩니다. 둘 다 대형 오브젝트에 많은 할당 패턴을 사용합니다. 나는. 그들은 (오래 실행되는) 루프에서 많은 객체들을 요청하고 있으며, 사용 후에 꽤 빨리 그 객체들을 해제합니다. 관리되는 앱은 즉시 호출되는 IDisposable()을 사용합니다. 관리되지 않는 사람은 소멸자를 이용합니다.누가 더 많은 이익을 얻는가? 관리/비 관리?

일부는 아니지만 일부 개체를 재사용 할 수 있습니다. 따라서 풀은 실행 속도를 높이고 메모리 조각화의 위험을 최소화하기 위해 고려됩니다.

풀링에서 더 많은 이익을 얻을 것으로 예상되는 애플리케이션과 그 이유는 무엇입니까?

@ 업데이트 : 이것은 수학 라이브러리에 관한 것입니다. 따라서 이러한 대형 객체는 값 유형의 배열입니다. 대부분 LOH에 충분합니다. 나는 긍정적이다. 풀을하면 관리 측면에서의 성과가 향상 될 것이다. 관리되는/관리되지 않는 환경을위한 많은 라이브러리가 존재합니다. 내가 아는 사람 중 누구도 그러한 풀링을 실제로하지 않습니다. 나는 왜 궁금해하니?

+0

숙제입니까? –

+0

나는 그것이 좋겠다고 생각했다. 그러나 우리가 제거 할 수없는 유일한 토론 주제 ... 나는 여기에 가능한 대답에 편견을주고 싶지 않지만 확실하게 생각을 가지고있다. 나는 그것이 확증되거나 반증되기를 희망한다. 또한 컨텍스트에 대한 개요를 얻는 데 도움이되어야합니다. – user492238

+2

대규모 관리 개체의 경우 Afaik 풀링이 매우 중요합니다. 감사합니다. – CodesInChaos

답변

1

이상한 기분,하지만 내가 직접 답변을하려고합니다 :

정말 내가 얘기하고 이해합니다. 그것에 대한 의견을 좀 주시겠습니까?

매우 큰 개체가 할당되고 해제되는 (루프) 경우 두 플랫폼에서 모두 조각화가 발생합니다. 관리되지 않는 앱의 경우 가상 주소 공간에서 직접 할당됩니다. 보통 작업 배열은 클래스 (C++)로 래핑되어 멋진 짧은 구문, 일부 참조 처리 및 소멸자에 대한 연산자 오버로드를 제공합니다. 범위를 벗어나면 이 즉시 해제됩니다. 그럼에도 불구하고 요청 된 배열은 항상 같은 크기가 아닙니다. 더 큰 배열이 요청되면 동일한 주소 블록을 재사용 할 수 없으므로 시간이 지남에 따라 조각화가 발생할 수 있습니다. 게다가, 블록을 찾는 방법이 없기 때문에 요청 된 어레이 길이를 정확하게 제공합니다. 운영 체제는 첫 번째 블록을 사용합니다.이 블록은 필요한만큼 커졌지만 나중에 예정된 더 큰 배열에 대한 요청을보다 완벽하게 채울 수 있었을지라도 충분히 클 수 있습니다. 어떻게 풀링이 그 상황을 개선 할 수 있었습니까?

작은 요청에 더 큰 배열을 사용하는 것이 상상할 수 있습니다. 클래스는 기본 배열의 실제 길이에서 외부 세계에 필요한 가상 길이로의 전환을 처리합니다. 풀은 OS와는 달리 "길이가 긴 첫 번째 어레이"를 제공하는데 도움이 될 수 있습니다. OS는 항상 정확한 길이를 제공합니다. 가상 주소 공간에 생성되는 구멍이 적기 때문에 조각화를 제한 할 수 있습니다. 반면에 전체 메모리 크기는 증가합니다. 거의 랜덤 한 할당 패턴의 경우, 풀링은 이익을 거의 내지 않을 것이지만 드문 기억 만 먹는다.

쪽에서는 상황이 더 나쁩니다. 우선, 조각화에 대한 두 가지 가능한 대상이 있습니다. 가상 주소 공간 관리 대상 대형 오브젝트 힙. 이 경우에는 개별 OS의 개별 집합을 개별적으로 수집해야합니다. 각 seqment는 주로 하나의 배열에 대해서만 사용됩니다 (여기서는 정말 큰 배열을 말합니다). 하나의 배열이 GC에 의해 해제되면 전체 세그먼트가 OS로 반환됩니다. 따라서 LOH에서는 조각화가 문제가되지 않습니다 (참조 : 내 자신의 생각과 VMMap을 사용한 경험적 관찰, 어떤 의견이라도 환영합니다!).

하지만 LOH 세그먼트가 가상 주소 공간에서 할당되기 때문에 관리되지 않는 응용 프로그램과 마찬가지로 조각화가 여기에도 문제가됩니다. 실제로 두 응용 프로그램의 할당 패턴은 OS의 메모리 관리자와 매우 유사해야합니다. (?) 하나의 구별 : 배열은 GC에 의해 동시에 해제됩니다. 그러나 "정말로 큰 배열"은 GC에 많은 압박을 줄 것입니다. 수집이 발생할 때까지 비교적 적은 수의 "정말로 큰 배열"만 동시에 유지 될 수 있습니다. 기본적으로 응용 프로그램은 일반적으로 GC에서 합리적인 시간 (약 5..45 %로 보임)을 소비합니다. 사실 모든 컬렉션이 값 비싼 Gen2 컬렉션이 될 것이고 거의 모든 할당이 이러한 Gen 2 컬렉션으로 귀결 될 것이기 때문입니다.

여기 풀링이 상당히 도움이 될 수 있습니다. 어레이가 OS로 해제되지 않고 풀에 수집되면 즉시을 추가 요청에 사용할 수 있습니다. (이것이 IDisposable이 관리되지 않는 리소스를위한 것이 아닌 이유 중 하나입니다.)프레임 워크/라이브러리는 배열이 초기에 풀에 배치되고 실제로 더 작은 크기가 필요한 경우에 더 큰 배열을 재사용 할 수 있도록하기 만하면됩니다.

3

먼저 큰 개체가 무엇인지에 대해 약간 고려하십시오. .net에서 큰 개체는 85,000 바이트 이상으로 간주됩니다. 당신은 정말로 큰 물체를 가지고 있습니까? 아니면 더 작은 물체의 매우 큰 그래프를 가지고 있습니까?

작은 개체의 그래프 인 경우 SOH (작은 개체 힙)에 저장됩니다. 이 경우 오브젝트를 작성하여 즉시 처리하도록하면 세대 별 모델을 가정하는 가비지 콜렉터의 최적화에서 최상의 이점을 얻을 수 있습니다. 제 말은 여러분이 물건을 만들어서 죽게하거나 영원히 지키게하는 것입니다. "잠시 동안"또는 다른 말로 풀링을하면, Gen 2 오브젝트를 정리하는 것이 비용이 많이 들기 때문에 (게이 2까지) 상위 세대로 승격되어 GC의 성능을 저하시킵니다. 그러나 2 세대의 영원한 물건은 비싸지 않다. 메모리 단편화에 대해 걱정하지 마십시오. 개체를 고정하는 것과 같은 상호 작용이나 멋진 작업을 수행하지 않는 한 GC는 메모리 조각화를 피하는 데 매우 효과적입니다. 즉 임시 세그먼트에서 해제되는 메모리를 압축합니다.

매우 큰 개체 (예 : 매우 큰 배열)가 실제로있는 경우 풀로 지불 할 수 있습니다. 그러나 배열에 작은 객체에 대한 참조가 포함되어 있다면 그 객체를 풀링하면 이전 단락에서 설명한 문제가 발생하므로 어레이를 정리해야합니다 (null을 가리키는 참조가 있음). (모든 반복이 필요합니까?).

그렇다고해서 IDisposable을 호출한다는 사실은 개체를 정리하는 것이 아닙니다. GC가 그렇게합니다. Dispose는 관리되지 않는 리소스를 정리합니다. 그렇지만 매우입니다. 중요한 것은 당신이 관리되지 않는 리소스를 즉각적으로 릴리스 할 것이기 때문에 그리고 또한 호출 할 필요가 없다는 것을 GC에 말하고 있기 때문에 클래스가 IDisposable을 구현하는 모든 객체에 Dispose를 호출하는 것입니다. 객체의 불필요한 승격으로 이어지는 객체의 파이널 라이저 (finalizer)는 우리가 보았 듯이 부정이 아닙니다.

결론은 GC가 물건을 할당하고 정리하는 데 정말 좋습니다. 당신이 정말로 무슨 일이 일어나고 있는지 알지 못한다면 대개 도움을 얻으려고 노력하면 성능이 저하됩니다.

Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework

Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework 2

Large Object Heap Uncovered

+0

. 나는 이미 개념을 알고 있었다. 내가 여러분의 게시물을 따르지 않는다고 생각하는 사람은 단 하나뿐입니다. Dispose()가 호출되면 using() 블록에서 해당 개체를 사용하는 것이 더 좋은 방법입니다. 문제의 ** 응용 프로그램은 ** 많은 '매우 큰'배열을 할당합니다. 불행히도이 질문에 대답하지 않습니다. 당신은 관리 측면만을 언급했습니다. 관리되지 않는 측면과 비교하면 어떻습니까? – user492238