>  기사  >  백엔드 개발  >  .Net 가비지 수집 및 대형 객체 처리

.Net 가비지 수집 및 대형 객체 처리

黄舟
黄舟원래의
2017-02-17 11:17:371312검색

영문 원문: Maoni Stephens, 편집자: Zhao Yukai(@玉凯Sir)

CLR 가비지 컬렉터는 개체의 크기에 따라 개체를 나눕니다. 그들이 차지하는 공간. 큰 물체와 작은 물체를 다루는 방법에는 큰 차이가 있습니다. 예를 들어, 메모리 조각 모음 - 메모리에서 큰 개체를 이동하는 데는 비용이 많이 듭니다. 가비지 수집기가 큰 개체를 처리하는 방법과 큰 개체가 프로그램 성능에 미치는 잠재적인 영향을 살펴보겠습니다.

대형 개체 힙 및 가비지 수집

.Net 1.0 및 2.0에서는 개체 크기가 85000바이트를 초과하면 A로 간주됩니다. 큰 물체. 이 수치는 성능 최적화 경험을 바탕으로 합니다. 개체에서 요청한 메모리 크기가 이 임계값에 도달하면 대형 개체 힙에 할당됩니다. 이것은 무엇을 의미합니까? 이를 이해하려면 .Net 가비지 수집 메커니즘을 이해해야 합니다.

대부분의 사람들이 알고 있듯이 .Net GC는 "세대" 단위로 데이터를 수집합니다. 프로그램에는 0세대, 1세대, 2세대의 세 가지 개체 세대가 있습니다. 0세대는 가장 어린 개체이고 2세대 개체는 생존 시간이 가장 길습니다. GC는 성능상의 이유로 세대별로 가비지를 수집합니다. 일반적으로 객체는 0세대에서 재활용됩니다. 예를 들어 ASP.NET 프로그램에서 각 요청과 연결된 개체는 요청이 끝날 때 재활용되어야 합니다. 재활용되지 않은 개체는 1세대 개체가 됩니다. 즉, 1세대 개체는 상주 메모리 개체와 곧 소멸될 개체 사이의 버퍼입니다.

세대 관점에서 대형 객체는 2세대 객체에 속합니다. 대형 객체는 2세대 재활용 중에만 처리되기 때문입니다. 특정 세대의 가비지 컬렉션이 실행되면 젊은 세대의 가비지 컬렉션도 동시에 실행됩니다. 예를 들어 1세대 가비지 수집을 수행하면 1세대와 0세대의 개체가 동시에 수집됩니다. 2세대 가비지 수집을 수행하면 1세대와 0세대의 개체가 수집됩니다.

세대는 가비지 컬렉터가 메모리 영역을 구분하는 세대입니다. 물리적 스토리지 관점에서 개체는 서로 다른 관리되는 힙에 할당됩니다. 관리되는 힙은 운영 체제의 가비지 수집기가 Windows API VirtualAlloc을 호출하여 할당한 메모리 영역입니다. CLR은 메모리를 로드할 때 두 개의 관리되는 힙, 즉 대형 개체 힙(LOH – 대형 개체 힙)과 소형 개체 쌍(SOH – 소형 개체 힙)을 초기화합니다.

메모리 할당 요청은 관리되는 개체를 해당 관리되는 힙에 배치하는 것입니다. 개체 크기가 85000바이트보다 작으면 SOH에 배치되고, 그렇지 않으면 LOH에 배치됩니다.

SOH의 경우 객체는 가비지 컬렉션을 수행한 후 다음 세대로 진입합니다. 즉, 처음으로 가비지 수집이 수행될 때 살아남은 객체가 2세대에 진입하고, 두 번째 가비지 수집 후에도 해당 객체가 여전히 가비지 수집되지 않으면 2세대 객체가 됩니다. 개체는 가장 오래된 개체이며 세대를 늘리지 않습니다.

가비지 수집이 실행되면 가비지 수집기는 작은 개체 힙의 조각 모음을 수행하고 남아 있는 개체를 함께 이동합니다. 대형 개체 힙의 경우 메모리 이동 비용이 높기 때문에 CLR 팀은 메모리를 사용하라는 다음 대형 개체 요청을 충족하기 위해 해당 힙을 지우고 재활용 개체 목록을 구성하기로 결정했습니다. 여유 메모리 블록.

대형 개체 힙은 .Net 4.0까지는 조각 모음이 수행되지 않지만 나중에 수행될 수 있다는 점에 항상 유의해야 합니다. 따라서 큰 개체를 할당하고 이동하지 않으려는 경우 고정 문을 사용할 수 있습니다.

다음 다이어그램은 소형 객체 힙 SOH의 재활용을 보여줍니다



1차 가비지 수집 전 위 그림에는 첫 번째 가비지 수집 후에 obj0-3 객체가 4개 있고 obj1과 obj3이 재활용되었으며 두 번째 가비지 수집 전에 obj2와 obj0이 함께 이동되었으며 두 번째 가비지 수집 후에 3개의 객체 obj4-6이 할당되었습니다. , 처음으로 가비지 수집을 수행한 후 obj2와 obj5를 재활용하고 obj4와 obj6을 obj0 옆으로 이동했습니다.

아래 사진은 대형 객체 힙 LOH 재활용의 개략도



보시다시피 가비지 수집이 수행되지 않습니다. 이전에는 1차 2세대 가비지 수집 이후 obj1과 obj2가 재활용된 후 obj1과 obj2가 차지한 공간이 함께 병합되었습니다. 할당, obj1은 재활용 후 해제된 공간이고 동시에 obj2가 할당되며 메모리 조각이 남습니다. 이 조각의 크기가 85000바이트 미만인 경우 이 조각은 이 프로그램의 수명 주기 동안 다시 사용할 수 없습니다.

적용할 대형 개체 공간을 수용할 만큼 대형 개체 힙에 사용 가능한 메모리가 충분하지 않은 경우 CLR은 먼저 운영 체제에서 메모리를 신청하려고 시도합니다. 일부 메모리를 해제하기 위해 2세대 재활용을 실행합니다.

2세대 가비지 컬렉션 중에는 VirtualFree를 통해 불필요한 메모리를 운영체제에 반환할 수 있습니다. 반품 절차는 아래 사진을 참고해주세요:



대형 물건은 언제 재활용되나요?

대형 객체를 언제 재활용할지 논의하기 전에 먼저 일반적인 가비지 수집 작업이 수행되는 시점을 살펴보겠습니다. 가비지 수집은 다음과 같은 상황에서 발생합니다.

1. 요청된 공간이 0세대의 메모리 크기 또는 대형 개체 힙의 임계값을 초과하는 경우 대부분의 관리형 힙 가비지 수집이 발생합니다.

2 . 프로그램 코드에서 GC.Collect 메소드 호출 시 GC.Collect 메소드 호출 시 GC.MaxGeneration 매개변수가 전달되면 대형 객체 힙의 가비지 수집을 포함하여 모든 생성 객체에 대한 가비지 수집이 수행됩니다. 🎜>

3. 운영체제의 메모리가 부족한 경우, 애플리케이션이 운영체제로부터 메모리 부족 알림을 받은 경우

4. 가비지 수집 알고리즘이 2세대 재활용이 효과적이라고 판단하면 2세대 가비지 수집이 시작됩니다

5. 각 객체 힙 세대에는 공간 크기 임계값을 차지하는 속성이 있습니다. 특정 세대에 객체를 할당하면 임계값에 가깝게 총 메모리 양이 늘어납니다. 힙 크기가 힙 임계값을 초과하면 가비지 수집이 발생합니다. 따라서 작은 개체나 큰 개체를 할당하면 0세대 힙이나 큰 개체 힙의 임계값이 사용됩니다. 가비지 수집기가 개체 생성을 1세대 또는 2세대로 늘리면 1세대와 2세대의 임계값이 사용됩니다. 이러한 임계값은 프로그램이 실행되는 동안 동적으로 변경됩니다.

대형 개체 힙이 성능에 미치는 영향

먼저 대형 개체 할당 비용을 살펴보겠습니다. CLR이 각각의 새 개체에 대해 메모리를 할당할 때 메모리가 지워지고 다른 개체에서 사용되지 않는지 확인해야 합니다. 이는 할당 비용이 정리 비용에 의해 완전히 제어된다는 것을 의미합니다(할당 중에 가비지 수집이 트리거되지 않는 한). 1바이트를 지우는 데 2사이클이 걸린다면 가장 작은 대형 객체를 지우는 데 170,000사이클이 걸린다는 뜻이다. 일반적으로 사람들은 매우 큰 개체를 할당하지 않습니다. 예를 들어 2GHz 시스템에 16M 개체를 할당하는 데는 메모리를 지우는 데 약 16ms가 걸립니다. 가격이 너무 높습니다.

재활용 비용을 살펴보겠습니다. 앞서 언급했듯이 대형 개체는 2세대 개체와 함께 재활용됩니다. 대형 객체나 2세대 객체가 차지하는 공간이 임계값을 초과하면 2세대 객체의 재활용이 시작됩니다. 대형 개체 힙이 임계값을 초과하여 2세대 재활용이 트리거되는 경우 2세대 개체 힙 자체에는 재활용할 수 있는 개체가 많지 않습니다. 2세대 힙에 객체가 많지 않다면 이는 큰 문제가 되지 않습니다. 그러나 2세대 힙이 크고 개체가 많은 경우 2세대 재활용이 과도하면 성능 문제가 발생할 수 있습니다. 대형 개체를 임시로 할당하면 가비지 수집을 실행하는 데 많은 시간이 걸립니다. 즉, 대형 개체를 계속 사용하고 나서 대형 개체를 해제하면 성능에 큰 부정적인 영향을 미치게 됩니다.

대형 개체 힙의 대형 개체는 일반적으로 배열입니다(한 개체가 매우 큰 경우는 드뭅니다). 개체의 요소가 강력한 참조인 경우 비용이 매우 높으며, 요소 간 상호 참조가 없으면 가비지 수집 중에 전체 배열을 탐색할 필요가 없습니다. 예를 들어 배열을 사용하여 이진 트리의 노드를 저장합니다. 한 가지 방법은 노드의 왼쪽 및 오른쪽 노드를 강력하게 참조하는 것입니다.

class Node
{
Data d;
Node left;
Node right;
}
 
Node[] binaryTree = new Node[num_nodes];

num_nodes가 큰 숫자인 경우 각 노드에는 최소한 두 개의 참조 요소가 있어야 합니다. 대안은 노드


class Node
{
Data d;
uint left_index;
uint right_index;
}

에 왼쪽 및 오른쪽 노드 요소의 배열 인덱스 번호를 저장하는 것입니다. 이 경우 요소 간의 참조 관계가 제거됩니다. ; BinaryTree [left_index]를 사용하여 참조된 노드를 얻을 수 있습니다. 가비지 수집기는 더 이상 가비지 수집을 수행할 때 관련 참조 요소를 볼 필요가 없습니다.

대형 개체 힙에 대한 성능 데이터 수집

대형 개체 힙과 관련된 성능 데이터를 수집하는 방법에는 여러 가지가 있습니다. 이러한 방법을 설명하기 전에 대형 개체 힙과 관련된 성능 데이터를 수집해야 하는 이유에 대해 설명하겠습니다.

특정 측면에서 성능 데이터를 수집하기 시작하면 해당 측면이 성능 병목 현상을 유발한다는 증거를 이미 찾았을 수도 있고, 모든 측면을 검색했지만 아무런 문제도 발견하지 못했을 수도 있습니다.

.Net CLR Memory 성능 카운터는 일반적으로 성능 문제를 찾을 때 고려해야 할 첫 번째 도구입니다. LOH와 관련된 카운터에는 2세대 컬렉션(2세대 힙 컬렉션 수) 및 대형 개체 힙 크기가 포함됩니다. 2세대 수집은 프로세스가 시작된 이후 발생한 2세대 가비지 수집 작업의 수를 보여줍니다. 대형 개체 힙 크기 카운터는 여유 공간을 포함하여 대형 개체 힙의 현재 크기를 표시합니다. 이 카운터는 메모리가 할당될 때마다 업데이트되는 것이 아니라 각 가비지 수집 작업 후에 업데이트됩니다.

아래 그림을 참고하여 Windows 성능 카운터에서 .Net CLR 메모리 관련 성능 데이터를 관찰할 수 있습니다


또한 프로그램을 통해 이러한 카운터의 값을 쿼리할 수 있습니다. 많은 사람들이 성능 병목 현상을 찾는 데 도움을 주기 위해 프로그램을 통해 성능 카운터를 수집합니다.

물론, winddbg 디버거를 사용하여 대형 개체 힙을 관찰할 수도 있습니다.

최종 알림: 지금까지 대형 개체 힙은 가비지 수집의 일부로 조각 모음되지 않았지만 이는 단지 clr의 구현 세부 사항일 뿐이며 프로그램 코드는 이 기능에 의존해서는 안 됩니다. 가비지 수집기에 의해 개체가 이동되지 않도록 하려면 고정 문을 사용하세요.

원본주소 : http://www.php.cn/

위는 .Net 가비지컬렉션 입니다. 대형 객체 처리 내용에 대해서는 PHP 중국어 웹사이트(www.php.cn)를 참고하여 더 많은 관련 내용을 확인하시기 바랍니다!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.