수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1621041
  • Today | 421
  • Yesterday | 482

1 Articles, Search for 'Garbage Collection'

  1. 2007/01/11 가비지 수집기 기본 및 성능 힌트
Programming/.NET2007/01/11 09:08

가비지 수집기 기본 및 성능 힌트

고수닷넷 - enoo님

Rico Mariani
Microsoft Corporation

2003년 4월


요약: .NET 가비지 수집기는 메모리를 적절하게 사용하고 장기적인 조각화 문제를 일으키지 않는 고속 할당 서비스를 제공합니다. 이 기사에서는 가비지 수집기의 작동 방법을 설명하고 가비지 수집 환경에서 발생할 수 있는 몇 가지 성능 문제를 살펴보도록 하겠습니다.

적용 대상:
  Microsoft .NET Framework

목차

소개
단순화된 모델
가비지 수집
성능
종료
결론

소개

가비지 수집기를 적절하게 사용하는 방법과 가비지 수집 환경에서 실행 시 발생할 수 있는 성능 문제를 이해하려면 가비지 수집기가 어떤 방식으로 작동하고 가비지 수집기의 내부 작업이 실행 프로그램에 어떤 영향을 주는지에 대한 기본 사항을 이해해야 합니다.

이 기사는 크게 두 부분으로 나뉘어져 있습니다. 첫 번째 부분에서는 단순화된 모델을 사용하여 공용 언어 런타임(CLR)의 가비지 수집기가 어떤 특성을 가지는지 설명하고 두 번째 부분에서는 이러한 구조가 성능에 어떤 영향을 줄 수 있는지에 대해 살펴봅니다.

단순화된 모델

설명을 용이하기 하기 위해 다음과 같은 관리 힙의 단순화된 모델이 있다고 가정합니다. 이 모델은 실제로 구현되는 것이 아니라는 점에 주의합니다.

사용자 삽입 이미지

그림 1. 관리 힙의 단순화된 모델

이 단순화된 모델의 규칙은 다음과 같습니다.

  • 가비지 수집 가능한 모든 객체는 하나의 인접한 주소 공간 범위에서 할당됩니다.
  • 힙의 작은 부분만 확인하여 대부분의 가비지를 제거할 수 있도록 힙은 여러 세대(뒤에서 자세히 설명)로 구분됩니다.
  • 한 세대 내의 모든 개체는 거의 동일한 나이를 가집니다.
  • 더 높은 번호의 세대는 더 오래된 개체가 있는 힙 영역을 나타냅니다. 개체는 오래될수록 더 안정적입니다.
  • 가장 오래된 개체는 가장 낮은 주소에 위치하며 새 개체가 만들어질 때마다 주소가 늘어납니다. 위의 그림 1에서는 밑으로 내려갈수록 주소가 늘어납니다.
  • 새 개체에 대한 할당 포인터는 메모리의 사용(할당) 영역과 사용되지 않은(사용 가능) 영역 간의 경계를 표시합니다.
  • 데드 개체를 제거하고 라이브 개체를 힙의 하위 주소 끝으로 이동하여 힙을 정기적으로 압축합니다. 이렇게 하면 새 개체가 만들어지는 다이어그램 아래쪽의 사용되지 않는 영역이 확장됩니다.
  • 적절한 위치를 위해 메모리의 개체 순서는 개체가 만들어진 순서를 유지합니다.
  • 힙의 개체 사이에는 간격이 존재하지 않습니다.
  • 사용 가능한 공간 중 일부만 커밋됩니다. 필요할 경우 예약된 주소 범위의 운영 체제에서 추가 메모리를 얻습니다.

가비지 수집

알아야 할 수집 중에서 가장 쉬운 종류는 전체 압축 가비지 수집입니다. 따라서 여기에서는 우선 이 수집에 대해 살펴보도록 하겠습니다.

전체 수집

전체 수집에서는 프로그램 실행을 중단하고 GC 힙의 모든 루트를 확인해야 합니다. 이러한 루트는 다양한 형태를 취하지만 대부분 힙을 가리키는 스택 및 전역 변수입니다. 루트로부터 시작하여 모든 개체를 방문하게 되며 방문한 모든 개체에 포함된 해당 개체를 표시하는 개체 포인터를 따라 이동합니다. 이러한 방식으로 수집기는 접근 가능한 개체 또는 라이브 개체를 확인합니다. 접근할 수 없는 다른 개체는 이제 폐기됩니다.

사용자 삽입 이미지

그림 2. GC 힙의 루트

접근할 수 없는 개체가 확인되고 나면 나중에 사용하기 위해 해당 공간을 회수할 수 있습니다. 즉, 이 시점에서 수집기의 목표는 라이브 개체를 위로 올리고 낭비된 공간을 제거하는 것입니다. 실행이 중단되면 수집기가 안전하게 이러한 개체를 모두 이동하고 새 위치로 올바르게 연결되도록 모든 포인터를 수정할 수 있습니다. 남아 있는 개체가 다음 세대 번호로 수준이 올라가면(즉, 세대의 경계가 업데이트되면) 실행을 다시 시작할 수 있습니다.

부분 수집

전체 가비지 수집을 매번 수행하기에 비용이 많이 들기 때문에 지금부터는 가비지 수집의 세대로부터 어떤 도움을 받을 수 있는지에 대해 살펴보도록 하겠습니다.

우선 매우 운이 좋은 가상의 경우를 가정해 보도록 하겠습니다. 최근에 전체 수집이 수행되었고 힙이 적절하게 압축되었다고 가정합니다. 이 경우, 프로그램 실행이 다시 시작되고 약간의 할당이 수행됩니다. 아주 많은 할당이 수행되어 충분한 할당이 이루어지면 메모리 관리 시스템은 수집 시간이 되었다는 것을 결정합니다.

이러한 상황에서 운이 좋게도 마지막 수집 이후 전체 실행 과정에서 이전 개체에 전혀 기록하지 않았으며 새로 할당된 0세대(gen0) 개체만 기록되었다고 가정해 봅니다. 이러한 경우는 가비지 수집 과정을 획기적으로 단순화할 수 있기 때문에 아주 운이 좋은 상황일 것입니다.

일반적인 전체 수집 대신에 모든 이전 개체(gen1, gen2)가 계속해서 라이브 상태이거나 최소한 이러한 개체가 작동 중이어서 확인할 필요가 없다고 가정할 수 있습니다. 또한 운이 좋게도 이러한 개체 중 기록된 것이 없기 때문에 이전 개체에서 새 개체로의 포인터가 존재하지 않습니다. 따라서 이 상황에서는 모든 루트를 보통 때와 같이 확인하고 이전 개체를 가리키는 루트가 있을 경우 단순히 이러한 루트를 무시할 수 있습니다. 다른 루트(gen0을 가리키는 루트)의 경우 평상시처럼 모든 포인터를 따라 이동하며 이전 개체로 향하는 내부 포인터는 항상 무시됩니다.

이러한 프로세스가 완료되면 이전 세대의 개체를 열지 않고 gen0의 모든 라이브 개체를 열 수 있습니다. 그런 다음 gen0 개체를 평상시처럼 폐기할 수 있으며 해당 메모리 영역을 위로 이동함으로써 이전 개체의 상태를 그대로 유지합니다.

이제 대부분의 데드 공간이 최신 개체에 존재한다는 것을 알 수 있으므로 상황이 매우 유리하다고 할 수 있습니다. 대부분의 클래스는 자체의 반환 값을 위한 임시 개체, 임시 문자열 및 혼합된 다른 유틸리티 클래스(예: 열거자 등)를 만듭니다. gen0을 보면 개체의 극히 일부만을 확인하여 대부분의 데드 공간을 간단하게 되돌리는 방법을 알 수 있습니다.

그러나 불행하게도 이전 개체가 약간이라도 변경되어 새 개체를 가리키는 것이 거의 대부분이기 때문에 이러한 방식을 사용할 만큼 운이 좋은 경우는 결코 없을 것입니다. 따라서 단순히 이전 개체를 무시하는 것만으로는 충분하지 않습니다.

세대에서 쓰기 장벽 사용

위에서 설명한 알고리즘을 실제로 작동하려면 이전 개체 중에서 수정된 개체를 알아야 합니다. 변경된 개체의 위치를 기억하기 위해 카드 테이블이라는 데이터 구조가 사용되며, 이 데이터 구조를 유지 관리하기 위해 관리 코드 컴파일러는 쓰기 장벽을 생성합니다. 이러한 두 가지 개념은 세대 기반 가비지 수집을 성공적으로 수행하기 위해 필수적입니다.

카드 테이블은 다양한 방법으로 구현될 수 있지만 가장 쉬운 방법은 카드 테이블을 비트 배열로 생각하는 것입니다. 카드 테이블의 각 비트는 힙의 메모리 범위(예: 128바이트)를 나타냅니다. 프로그램이 개체를 일부 주소에 쓸 때마다 쓰기 장벽 코드는 어떤 128바이트 청크가 기록되었는지 계산한 다음 카드 테이블에서 해당 비트를 설정해야 합니다.

이 메커니즘이 제대로 작동하면 이제 수집 알고리즘을 다시 적용할 수 있습니다. gen0 가비지 수집을 수행하는 중이면 위에서 설명한 알고리즘을 사용하여 이전 세대에 대한 모든 포인터를 무시할 수 있지만, 그 이후에 카드 테이블에서 수정된 것으로 표시된 청크의 모든 개체에서 모든 개체 포인터를 확인해야 합니다. 이러한 포인터는 루트처럼 다루어져야 합니다. 이러한 포인터를 함께 고려할 경우 gen0 개체를 올바르게 수집할 수 있을 것입니다.

카드 테이블이 이미 가득 차 있는 경우 이 방식은 전혀 도움이 되지 않지만 이전 세대의 포인터 중 극소수만 실제로 수정되기 때문이 이 방식을 통해 상당한 성과를 거둘 수 있습니다.

성능

지금까지는 작업 방법에 대한 기본 모델을 설명했으며 이제 속도 저하의 원인이 되는 몇 가지 문제를 살펴보도록 하겠습니다. 이러한 문제를 확인하면 수집기의 성능을 극대화하기 위해 피해야 할 것이 무엇인지 알 수 있을 것입니다.

할당이 너무 많음

성능을 저하시킬 수 있는 가장 기본적인 문제입니다. 가비지 수집기를 사용하면 새 메모리를 매우 신속하게 할당할 수 있습니다. 위의 그림 2에서 볼 수 있듯이 일반적으로 "할당된" 쪽에서 새 개체를 위한 공간을 만들 수 있도록 할당 포인터를 이동하는 것이 필요한데, 이 작업은 그리 빠르지 않습니다. 그러나 머지 않아 가비지 수집이 이루어져야 하므로 특별한 경우가 아니라면 이전보다 나중에 이 작업을 수행하는 것이 좋습니다. 따라서 새 개체 하나를 신속하게 만들 수 있지만 지금 만들고 있는 새 개체가 꼭 필요하고 적합한 것인지 확인해야 할 수 있습니다.

이것은 당연한 충고처럼 들릴 수 있지만 실제로는 작성하는 한 줄의 코드로 인해 많은 할당이 트리거될 수 있다는 사실을 잊기 쉽습니다. 예를 들어 어떤 종류의 비교 함수를 작성하고 있으며 개체에 키워드 필드가 있는 상태에서 지정된 순서대로 키워드의 대/소문자를 구분하는 비교를 수행하려 한다고 가정해 보십시오. 그런데 첫 번째 키워드가 너무 짧아서 전체 키워드 문자열을 비교하지 못할 수 있습니다. 이 경우 String.Split를 사용하여 키워드 문자열을 여러 조각으로 분리한 다음 일반 대/소문자 비교를 사용하여 각 조각을 비교하는 방법을 선택할 수 있습니다. 그럴듯하지 않습니까

그러나 이 방법을 사용하는 것은 그리 좋은 생각이 아닙니다. String.Split가 문자열 배열을 만들게 되는데, 이는 배열에 대한 추가 개체 외에 원래 키워드 문자열에 있는 모든 키워드에 대해 새 문자열 개체가 하나씩 생긴다는 것을 의미합니다. 즉 정렬 컨텍스트에서 이 작업을 수행 중인 경우 비교가 많아지고 두 줄 비교 함수에서 매우 많은 수의 임시 개체를 만듭니다. 갑자기 가비지 수집기가 사용자를 대신하여 매우 바쁘게 작동하기 시작하며 가장 적절한 수집 방식일지라도 정리해야 할 쓰레기가 너무 많이 발생합니다. 따라서 할당을 전혀 요구하지 않는 비교 함수를 작성하는 것이 좋습니다.

너무 큰 할당

malloc() 같은 일반 할당자를 사용할 경우 프로그래머는 할당 비용이 상당히 크다는 것을 알기 때문에 가능한 한 malloc()을 조금만 호출하는 코드를 작성합니다. 이것은 청크로 할당하는(흔히 필요한 개체를 추론적으로 할당하는) 방식이 되며, 따라서 더 적은 할당을 수행할 수 있습니다. 그런 다음, 미리 할당된 개체를 일종의 풀에서 수동으로 관리함으로써 고속 사용자 지정 할당자를 효과적으로 만들게 됩니다.

관리 환경에서는 다음과 같은 몇 가지 이유로 인해 이 작업 방식이 거의 사용되지 않습니다.

첫째, 할당을 수행하는 비용이 매우 낮습니다. 일반 할당자에서처럼 사용 가능한 블록에 대한 검색이 없기 때문에 필요한 것은 단지 사용 가능한 영역과 할당된 영역 간의 경계를 이동하는 것입니다. 할당 비용이 낮다는 것은 풀링을 수행해야 할 가장 중요한 이유가 없다는 것을 의미합니다.

둘째, 미리 할당하는 경우 실제 요구에 필요한 것보다 많은 할당을 수행하게 됩니다. 이것은 불필요한 추가 가비지 수집을 수행하게 되는 원인이 될 수 있습니다.

마지막으로 가비지 수집기는 사용자가 수동으로 재활용하는 개체에 대한 공간을 회수할 수 없습니다. 이는 전역적 관점에서 볼 때 현재 사용되지 않는 것을 비롯한 모든 개체가 계속 라이브 상태이기 때문입니다. 따라서 사용 중이 아니지만 사용할 준비가 된 개체를 유지하기 위해 많은 양의 메모리가 낭비될 수 있습니다.

그러나 이것은 미리 할당하는 것이 항상 나쁘다는 의미는 아닙니다. 예를 들어 특정 개체를 초기에 함께 할당하기 위해 미리 할당을 선택할 수 있지만 관리되지 않는 코드에서처럼 일반 전략으로서의 가치는 덜합니다.

포인터가 너무 많음

많은 포인터가 있는 데이터 구조를 만드는 경우 두 가지 문제가 발생합니다. 첫 번째 문제는 많은 개체 쓰기가 존재한다는 것이며(그림 3 참조) 두 번째 문제는 해당 데이터 구조를 수집할 때 가비지 수집기가 모든 포인터를 따라가고 필요한 경우 이동에 따라 이러한 포인터를 모두 변경한다는 것입니다. 데이터 구조가 오래 지속되며 많이 변경되지 않을 경우 수집기는 전체 수집이 수행될 경우에만(gen2 수준에서) 이러한 포인터를 모두 방문해야 합니다. 그러나 처리 트랜잭션의 일부분과 같은 일시적인 구조를 만드는 경우에는 비용을 훨씬 더 자주 지불하게 됩니다.

사용자 삽입 이미지

그림 3. 포인터가 너무 많은 데이터 구조

포인터가 너무 많은 데이터 구조는 또한 가비지 수집 시간과 관련되지 않은 다른 문제를 가질 수 있습니다. 앞에서 언급한 것과 같이 개체를 만들 때 개체는 할당 순서대로 인접하여 할당됩니다. 예를 들어 파일에서 정보를 복원하여 복잡하고 큰 데이터 구조를 만들 때 이 방법은 매우 효과적입니다. 별개의 데이터 형식이 있더라도 모든 개체는 메모리에서 인접하게 되며 결과적으로 프로세서가 이러한 개체를 신속하게 액세스하는 데 도움이 됩니다. 그러나 시간이 흐르고 데이터 구조가 수정되면 새 개체를 이전 개체에 연결해야 합니다. 이러한 새 개체는 훨씬 더 나중에 만들어지기 때문에 메모리에서 원래 개체의 근처에 있지 않습니다. 가비지 수집기가 메모리를 압축하지 않더라도 개체는 메모리에서 혼합되지 않으며 단순히 함께 이동함으로써 낭비된 공간을 제거합니다. 시간이 지나면서 점점 더 무질서해질 수 있는데, 이 경우 적절하게 압축된 전체 데이터 구조의 최신 복사본을 만들어 수집기에서 때가 되면 오래된 것을 폐기하도록 할 수 있습니다.

루트가 너무 많음

물론 가비지 수집기는 수집을 수행할 때 루트에 대해 특수한 조치를 취해야 합니다. 즉, 루트는 항상 열거된 다음 적절하게 고려되어야 합니다. gen0 수집은 고려할 많은 양의 루트를 제공하지 않은 경우에 한하여 신속하게 수행될 수 있습니다. 자체의 로컬 변수 간에 많은 개체 포인터를 가진 매우 재귀적인 함수를 만들려는 경우 결과적으로 많은 비용이 초래될 수 있습니다. 이 비용은 이러한 모든 루트를 고려해야 한다는 사실 뿐만 아니라 그리 오래 지속되지 않는 매우 많은 수의 gen0 개체 때문에 루트가 발생합니다(이에 대해서는 뒤에 설명됨).

개체 쓰기가 너무 많음

앞에서 설명한 내용 중에는 관리되는 프로그램이 개체 포인터를 수정할 때마다 쓰기 장벽 코드가 트리거된다는 내용이 있었습니다. 이것은 다음과 같은 두 가지 이유로 문제가 될 수 있습니다.

첫째, 쓰기 장벽 비용이 처음 시도하려던 작업의 비용과 유사할 수 있습니다. 예를 들어 일종의 열거자 클래스에서 간단한 작업을 수행할 경우 일부 키 포인터를 기본 수집에서 모든 단계의 열거자로 이동해야 할 수 있습니다. 쓰기 장벽으로 인해 이러한 포인터를 복사하는 비용이 두 배로 늘어나고 열거자에서 루프당 한 번 이상 복사 작업을 수행해야 할 수 있으므로 사용자는 이러한 상황이 발생하는 것을 원치 않을 것입니다.

둘째, 이전 개체에 기록하는 경우 쓰기 장벽을 트리거하는 것은 문제를 두 배로 악화시킵니다. 이전 개체를 수정하는 경우 위에서 설명한 것처럼 실제로는 다음 가비지 수집이 발생할 때 검사할 추가 루트를 만듭니다. 이전 개체를 충분히 수정한 경우 가장 최신의 세대만 수집하는 것과 관련된 일반적인 속도 향상이 제공되지 않습니다.

이러한 두 가지 이유는 물론 모든 프로그램에서 너무 많은 쓰기 작업을 수행하지 않는 일반적인 이유와 관련이 있습니다. 특별한 경우가 아니라면 메모리(실제로 읽기 또는 쓰기)를 가능한 적게 사용하여 프로세서의 캐시를 더 경제적으로 이용하는 것이 바람직합니다.

오래 지속되는 개체가 너무 많음

마지막으로 세대 가비지 수집기의 가장 큰 문제는 오래 지속되지도 않고 일시적이지도 않은 개체가 너무 많이 만들어진다는 것입니다. 이러한 개체는 계속 필요하지만 가장 싼 gen0 수집에서 정리되지 않기 때문에 많은 문제를 발생시킬 수 있으며 계속 사용하지만 곧 사라지기 때문에 gen1 수집에서 살아남을 수 있습니다.

개체가 gen2 수준에 도달하면 전체 수집만 개체를 제거할 수 있지만 전체 수집은 많은 비용이 들기 때문에 가비지 수집기는 가능한 한 전체 수집을 지연시킨다는 점에 문제가 있습니다. 따라서 "오래 지속되는" 개체가 많은 경우 gen2가 매우 빠른 속도로 증가하여 결과적으로 사용자가 원하는 만큼 신속하게 정리되지 않을 수 있습니다. 또한 정리 시점이 되었을 때 사용자가 원하는 것보다 훨씬 더 많은 비용이 들 것입니다.

이러한 종류의 개체를 방지하기 위한 최선의 방법은 다음과 같습니다.

  1. 사용 중인 임시 공간의 양에 주의하면서 가능한 한 적은 개체를 할당합니다.
  2. 더 오래 지속되는 개체의 크기를 최소로 유지합니다.
  3. 스택에서 가능한 한 적은 개체 포인터를 유지합니다(이러한 포인터는 루트임).

이러한 작업을 수행할 경우 gen0 수집이 훨씬 더 효과적으로 이루어지며 gen1은 그렇게 빠른 속도로 증가하지 않습니다. 결과적으로 gen1 수집을 더 적게 수행할 수 있으며 gen1 수집을 수행하는 것이 적합할 경우 중간 수명 개체는 이미 데드 상태일 것이며 해당 시점에 저렴하게 복구할 수 있습니다.

안정적인 작업을 수행하는 도중에 별다른 문제가 발생하지 않을 경우 gen2 크기는 전혀 증가하지 않습니다.

종료

지금까지 간단한 할당 모델과 함께 몇 가지 주제를 다루었으므로 이제 더 중요한 현상인 종료자 및 종료의 비용에 대해 논의할 수 있도록 약간 복잡한 상황을 살펴보려고 합니다. 간단히 말해서 종료자는 모든 클래스에 존재할 수 있습니다. 즉, 종료자는 가비지 수집기가 데드 개체의 메모리를 회수하기 전에 해당 개체에 대해 호출하기로 약속하는 선택적 구성원입니다. C#에서는 ~Class 구문을 사용하여 종료자를 지정합니다.

종료가 수집에 미치는 영향

가비지 수집기는 데드 상태이지만 종료해야 할 필요가 있는 개체를 처음 만나면 해당 개체의 공간을 회수하기 위한 시도를 포기해야 합니다. 대신, 해당 개체가 종료가 필요한 개체 목록에 추가되면 수집기는 종료가 완료될 때까지 개체 내의 모든 포인터가 유효한 상태로 있는지 확인해야 합니다. 이것은 기본적으로 종료가 필요한 모든 개체가 수집기의 관점에서 임시 루트 개체와 동일하다고 말하는 것과 같습니다.

수집이 완료되면 적절하게 이름이 지정된 종료 스레드는 종료가 필요한 개체 목록을 확인하면서 종료자를 호출합니다. 이 작업이 끝나면 개체는 다시 데드 상태가 되며 정상적인 방법으로 자연스럽게 수집됩니다.

종료 및 성능

종료에 대한 이러한 기본 이해를 가지고 몇 가지 중요한 사항을 추론할 수 있습니다.

첫째, 종료가 필요한 개체는 그렇지 않은 개체보다 오래 지속됩니다. 실제로 종료가 필요한 개체는 훨씬 더 오래 지속될 수 있습니다. 예를 들어 gen2의 개체에 종료가 필요하다고 가정해 보십시오. 이런 경우 종료가 예약되지만 개체는 여전히 gen2에 있기 때문에 다음 gen2 수집이 발생할 때까지 해당 개체를 다시 수집하지 않습니다. 이 기간은 실제로 매우 길어질 수 있는데, 이는 gen2 수집에 많은 비용이 소요되므로 특별한 경우가 아닌 이상 이러한 수집이 자주 발생하는 것을 원하지 않기 때문입니다. 종료가 필요한 이전 개체는 자체의 공간이 회수되기 전에 수십 개에서 수백 개의 gen0 수집을 대기해야 할 수 있습니다.

둘째, 종료가 필요한 개체는 이차적인 피해를 가져옵니다. 내부 개체 포인터가 유효한 상태로 있어야 하므로 직접적으로 종료가 필요한 개체가 메모리에서 머무를 뿐만 아니라 개체가 직접 및 간접적으로 참조하는 모든 것이 메모리에 남게 됩니다. 대규모 트리의 개체가 종료가 필요한 단일 개체에 의해 앵커된 경우 방금 설명한 것처럼 오랜 시간동안 전체 트리가 계속 남아 있게 됩니다. 따라서 종료자를 꼭 필요할 경우만 사용하고 가능한 적은 내부 개체 포인터를 가진 개체에 종료자를 두는 것이 중요합니다. 방금 언급한 트리 예제에서는 종료가 필요한 리소스를 별도의 개체로 이동하고 해당 개체에 대한 참조를 트리의 루트에 유지함으로써 문제를 쉽게 방지할 수 있습니다. 적절하게 변경하면 개체 하나(가능한 작은 개체)만 계속 남아 있으며 종료 비용이 최소화됩니다.

마지막으로 종료가 필요한 개체는 종료자 스레드를 위한 작업을 만듭니다. 종료 프로세스가 복잡한 경우 하나의 종료자 스레드가 이러한 단계를 수행하는 데 많은 시간을 소비하여 작업 백로그가 발생하며, 결과적으로 더 많은 개체가 종료를 대기하면서 계속 남아 있게 됩니다. 따라서 종료자가 가능한 적은 작업을 수행하는 것이 매우 중요합니다. 또한 모든 개체 포인터가 종료 도중 유효한 상태를 유지하지만 이러한 포인터가 이미 종료된 개체를 가리킬 수 있고 이로 인해 유용성이 떨어질 수 있다는 것에 주의합니다. 일반적으로 포인터가 유효하더라도 종료 코드에서 다음 개체 포인터를 사용하지 않는 것이 가장 안전합니다. 안전하고 짧은 종료 코드 경로가 최선의 방법입니다.

IDisposable 및 Dispose

항상 종료해야 하는 개체에 대해 IDisposable 인터페이스를 구현하여 이러한 비용을 방지할 수 있는 경우가 많이 있습니다. 이 인터페이스는 프로그래머가 수명을 잘 알고 있으며 실제로 많이 발생하는 리소스를 회수하기 위한 다른 방법을 제공합니다. 물론 IDisposable 인터페이스를 구현하는 것은 개체가 단순히 메모리만 사용하기 때문에 종료나 삭제가 전혀 필요하지 않은 경우에도 좋지만 특히 종료가 필요하며 개체의 명시적 관리가 대개 간단하고 실제적으로 수행되는 경우에 종료 비용을 없애거나 최소한 줄일 수 있는 뛰어난 방법입니다.

C# 언어에서 다음 패턴은 매우 유용하게 사용될 수 있습니다.

class X:  IDisposable
{
   public X(...)
   {
   ... initialize resources ... 
   }

   ~X()
   {
   ... release resources ... 
   }

   public void Dispose()
   {
// ~X()를 호출하는 것과 동일
        Finalize(); 

// 나중에 종료할 필요가 없음
System.GC.SuppressFinalize(this); 
   }
};

여기에서 Dispose를 수동으로 호출함으로써 수집기는 개체의 지속적 유지와 종료자 호출을 수행할 필요가 없습니다.

결론

.NET 가비지 수집기는 메모리를 적절하게 사용하고 장기적인 조각화 문제를 일으키지 않는 고속 할당 서비스를 제공하지만 최적 성능을 저하시키는 여러 원인을 제공할 수 있습니다.

할당자를 최대한 활용하려면 다음과 같은 작업 방식을 고려해야 합니다.

  • 특정 데이터 구조와 함께 사용할 모든 메모리나 가능한 많은 메모리를 동시에 할당합니다.
  • 복잡성에 거의 영향을 주지 않으면서 없앨 수 있는 임시 할당을 제거합니다.
  • 특히 이전 개체에 쓰기를 하는 경우 개체 포인터가 기록되는 횟수를 최소화합니다.
  • 데이터 구조에서 포인터의 밀도를 줄입니다.
  • "리프" 개체에서만 사용되도록 종료자의 사용을 최대한 제한합니다. 필요한 경우 이 작업을 돕기 위해 개체를 분리합니다.

정기적으로 주요 데이터 구조를 검토하고 할당 프로필러 같은 도구로 메모리 사용 프로필을 수행하면 장기적으로 볼 때 메모리 사용을 효과적으로 유지하고 가비지 수집기의 작동을 최적화할 수 있을 것입니다.

".NET" 카테고리의 다른 글
  • .NET의 Enterprise Service(COM+) 이해 (0)2007/01/11
  • Microsoft Application Blocks for .NET (0)2007/01/11
  • 가비지 수집기 기본 및 성능 힌트 (0)2007/01/11
  • 닷넷 리모팅 서비스에 대한 이해3 - 리모트객체의... (0)2007/01/10
  • 닷넷 리모팅 서비스에 대한 이해2 (iis를 호스트로... (0)2007/01/10
2007/01/11 09:08 2007/01/11 09:08
Posted by webdizen
Tags Garbage Collection
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/2572

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1  Next»

RSS HanRSS
Blog Image
webdizen
이 곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (2998)
Webdizen (134)
Life (6)
Diary (16)
Blog (9)
IDEA (1)
Travel (10)
Book (14)
Photo (7)
Movie (7)
Music (13)
Leisure Sports (10)
Funny (5)
Hardware (119)
Software (120)
Windows (5)
Unix & Linux (119)
Installation (4)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (805)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (3)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (3)
Network (66)
Web (78)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • JavaFX
  • 레퍼런스
  • 윈도우즈 종료
  • Solar PHP
  • J&B 레어
  • eCommerce
  • 돌체 비타 로소
  • 국가대표
  • 연결 프로그램 찾기 다이얼로그
  • Designing
  • 에어론 의자
  • 미러링
  • 몽랑볼 볼리노
  • Receive
  • 연관 분석
  • LaTeX
  • Email
  • IPC
  • 문자열 검색
  • Python

Recent Articles

  • ASCII Code의 CRLF 제거 방법.
  • Hadoop 에서 c++ API 이용시....
  • Ubuntu Linux에서 Hadoop 구....
  • 내 심장을 한껏 뛰게한 "국가....
  • 스타 스키마 데이터베이스 설....

Recent Comments

  • ■ 온라인카지노 ▶ http://L....
    asdf 11/21
  • 그리고 혹시 해외여행자보험....
    kim 11/05
  • ★★실제 바다게임장과 똑같....
    asdf 11/04
  • sbsyama.co.to← 짱5000만당....
    asdf 11/04
  • ♡KicaZ??o(???) 바카라사....
    fdsf3fass 11/03

Recent Trackbacks

  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 02/11
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007
  • 마방진을 구하는 프로그램.
    Oranzie's BLOG 3 2007

Archive

  • 2009/09 (3)
  • 2009/08 (1)
  • 2009/03 (1)
  • 2009/02 (9)
  • 2009/01 (13)

Calendar

«   2009/11   »
일 월 화 수 목 금 토
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Rodman®
      • ■ Feel So Good~! ■
      • 까만 나비
      • 나를 가꾸는 시간.
      • 나만의 즐거움~~!
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.