답변:
가장 좋은 방법은 가비지 수집을 강제하지 않는 것입니다.
MSDN에 따르면 :
"Collect를 호출하여 가비지 수집을 강제 할 수 있지만 성능 문제가 발생할 수 있으므로 대부분 피해야합니다."
그러나 Collect () 호출이 부정적인 영향을 미치지 않는지 확인하기 위해 코드를 안정적으로 테스트 할 수 있다면 계속 진행하십시오.
더 이상 필요하지 않을 때 개체가 정리되었는지 확인하십시오. 사용자 정의 개체가있는 경우 "using 문"및 IDisposable 인터페이스 사용을 살펴보십시오.
이 링크에는 메모리 / 가비지 수집 등을 해제하는 것과 관련하여 실용적인 조언이 있습니다.
TargetOfInvocationNullException.
이런 식으로보세요. 음식물 쓰레기통이 10 % 일 때 버리는 것이 더 효율적입니까, 아니면 버리기 전에 가득 채우는 것이 더 효율적입니까?
가득 채우지 않으면 외부 쓰레기통을 오가는 시간을 낭비하게됩니다. 이는 GC 스레드가 실행될 때 발생하는 것과 유사합니다. 모든 관리 스레드는 실행되는 동안 일시 중지됩니다. 그리고 내가 착각하지 않으면 GC 스레드를 여러 AppDomain간에 공유 할 수 있으므로 가비지 컬렉션은 모두에 영향을줍니다.
물론, 휴가를 떠날 예정이라면 곧 쓰레기통에 아무것도 추가하지 않을 상황에 처할 수도 있습니다. 그러면 외출하기 전에 쓰레기를 버리는 것이 좋습니다.
이것은 GC를 강제하는 것이 도움이 될 수있는 한 번일 수 있습니다. 프로그램이 유휴 상태이면 할당이 없기 때문에 사용중인 메모리가 가비지 수집되지 않습니다.
가장 좋은 방법은 대부분의 경우 가비지 수집을 강제하지 않는 것입니다. (내가 작업 한 모든 시스템에 가비지 수집을 강제하고 해결하면 가비지 수집을 강제 할 필요가 없어지고 시스템 속도가 크게 빨라지는 밑줄 문제가있었습니다.)
가비지 수집기보다 메모리 사용량에 대해 더 많이 알고 있는 몇 가지 경우 가 있습니다 . 이는 다중 사용자 응용 프로그램이나 한 번에 하나 이상의 요청에 응답하는 서비스에서는 해당되지 않을 수 있습니다.
그러나 일부 배치 유형 처리 에서는 GC보다 더 많이 알고 있습니다. 예를 들어 응용 프로그램을 고려하십시오.
당신은 할 수 있습니다 각 파일을 처리 한 후에는 전체 가비지 컬렉션을 강제해야 테스트 (주의 후) 케이스를 만들 수.
또 다른 경우는 일부 항목을 처리하기 위해 몇 분마다 깨어나고 수면 상태를 유지하지 않는 서비스입니다 . 그런 다음 잠들기 직전에 전체 수집을 강제하는 것이 가치 가 있을 수 있습니다.
컬렉션 강제를 고려하는 유일한 경우는 최근에 많은 개체가 생성되었고 현재 참조되는 개체가 거의 없다는 것을 알고있을 때입니다.
GC를 강제하지 않고도 이러한 유형에 대한 힌트를 줄 수있을 때 가비지 수집 API를 사용하는 것이 좋습니다.
" Rico Mariani의 Performance Tidbits " 도 참조하십시오.
Rico Mariani 가 제시 한 예가 좋았다고 생각합니다 . 애플리케이션 상태에 큰 변화가있는 경우 GC를 트리거하는 것이 적절할 수 있습니다. 예를 들어 문서 편집기에서 문서가 닫힐 때 GC를 트리거해도 괜찮을 수 있습니다.
절대적인 프로그래밍에 대한 일반적인 지침은 거의 없습니다. 절반의 시간 동안 누군가가 '당신이 잘못하고있다'고 말할 때, 그들은 단지 일정량의 교리를 내뿜는 것입니다. C에서는 자체 수정 코드 또는 스레드와 같은 것에 대한 두려움이 있었으며 GC 언어에서는 GC를 강제하거나 GC가 실행되지 않도록합니다.
대부분의 지침과 좋은 경험 법칙 (그리고 좋은 디자인 관행)의 경우처럼, 확립 된 표준을 우회하는 것이 합리적 일 경우는 드뭅니다. 당신은 당신이 사건을 이해하고, 당신의 사건이 정말로 일반적인 관행의 폐지를 요구하고, 당신이 유발할 수있는 위험과 부작용을 이해하고 있음을 확신해야합니다. 그러나 그러한 경우가 있습니다.
프로그래밍 문제는 매우 다양하며 유연한 접근 방식이 필요합니다. 가비지 수집 언어에서 GC를 차단하는 것이 합리적이며 자연스럽게 발생하기를 기다리지 않고 트리거하는 것이 합당한 경우를 보았습니다. 95 %의 경우, 이들 중 하나는 문제에 제대로 접근하지 못했다는 신호가 될 것입니다. 그러나 20 명 중 1 번, 아마도 그것을위한 유효한 사례가있을 것입니다.
나는 가비지 수집을 능가하려고하지 않는 법을 배웠다. 즉, using파일 I / O 또는 데이터베이스 연결과 같은 관리되지 않는 리소스를 다룰 때 키워드 사용을 고수 합니다.
이것이 모범 사례인지 확실하지 않지만 루프에서 많은 양의 이미지로 작업 할 때 (예 : 많은 그래픽 / 이미지 / 비트 맵 개체를 만들고 처리) 정기적으로 GC.Collect를 사용합니다.
나는 GC가 프로그램이 (대부분) 유휴 상태 일 때만 실행되고 집중적 인 루프의 중간에 있지 않을 때만 실행된다는 것을 읽은 것 같아서 수동 GC가 의미가있는 영역처럼 보일 수 있습니다.
최근에 수동 호출이 필요한 한 가지 경우 GC.Collect()는 작은 관리 C ++ 개체로 래핑 된 큰 C ++ 개체로 작업 할 때였으며, 이는 차례로 C #에서 액세스되었습니다.
사용 된 관리되는 메모리의 양이 무시할 수 있었기 때문에 가비지 수집기가 호출되지 않았지만 사용 된 관리되지 않는 메모리의 양이 엄청났습니다. Dispose()객체를 수동으로 호출 하려면 객체가 더 이상 필요하지 않은시기를 추적해야하는 반면, 호출 GC.Collect()하면 더 이상 참조되지 않는 객체가 정리됩니다 .....
GC.AddMemoryPressure (ApproximateSizeOfUnmanagedResource)은 생성자와 나중에 종료 자 GC.RemoveMemoryPressure(addedSize)에서 호출하는 것 입니다 . 이렇게하면 수집 할 수있는 관리되지 않는 구조의 크기를 고려하여 가비지 수집기가 자동으로 실행됩니다. stackoverflow.com/questions/1149181/…
나는 당신이 이미 모범 사례를 나열했다고 생각하며 정말로 필요하지 않는 한 그것을 사용하지 마십시오. 이러한 질문에 먼저 답해야 할 경우 잠재적으로 프로파일 링 도구를 사용하여 코드를 더 자세히 살펴볼 것을 강력히 권장합니다.
프로그램에 메모리 누출이없고 객체가 축적되어 다음과 같은 이유로 Gen 0에서 GC-ed 될 수 없다고 가정합니다. 1) 오랫동안 참조되므로 Gen1 및 Gen2에 들어가십시오. 2) 큰 개체 (> 80K)이므로 LOH (Large Object Heap)로 이동합니다. 그리고 LOH는 Gen0, Gen1 및 Gen2 에서처럼 압축을 수행하지 않습니다.
".NET Memory"의 성능 카운터를 확인하면 1) 문제가 실제로 문제가 아님을 알 수 있습니다. 일반적으로 10 개의 Gen0 GC마다 1 개의 Gen1 GC가 트리거되고 10 개의 Gen1 GC마다 1 개의 Gen2 GC가 트리거됩니다. 이론적으로 GC0에 압력이 없으면 GC1 및 GC2는 GC-ed 될 수 없습니다 (프로그램 메모리 사용량이 실제로 연결되어있는 경우). 나에게는 결코 일어나지 않습니다.
문제 2)의 경우 ".NET Memory"성능 카운터를 확인하여 LOH가 비대 해지고 있는지 확인할 수 있습니다. 문제가 실제로 문제인 경우이 블로그에서 http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx를 제안하는 것처럼 대형 개체 풀을 만들 수 있습니다 .
큰 개체는 0 세대가 아닌 LOH (대형 개체 힙)에 할당됩니다. Gen 0으로 가비지 수집되지 않는다는 말이 맞습니다. 전체 GC주기 (0, 1, 2 세대)가 발생할 때만 수집됩니다.
즉, 다른 쪽에서는 GC가 큰 개체로 작업하고 메모리 압력이 높아질 때 더 적극적으로 메모리를 조정하고 수집 할 것이라고 믿습니다.
수집 여부와 상황을 말하기는 어렵습니다. 많은 컨트롤 등이있는 대화 창 / 양식을 폐기 한 후 GC.Collect ()를 수행했습니다. (양식과 해당 컨트롤이 비즈니스 개체의 많은 인스턴스를 생성하고 많은 데이터를로드하기 때문에 2 세대로 끝나기 때문입니다. 분명히 큰 물체), 그러나 실제로 그렇게함으로써 장기적으로 긍정적이거나 부정적인 영향을 느끼지 못했습니다.
추가하고 싶습니다 : GC.Collect () (+ WaitForPendingFinalizers ()) 호출은 이야기의 일부입니다. 다른 사람들이 올바르게 언급했듯이 GC.COllect ()는 비 결정적 컬렉션이며 GC 자체 (CLR)의 재량에 맡겨집니다. WaitForPendingFinalizers에 대한 호출을 추가하더라도 결정적이지 않을 수 있습니다. 이 msdn 링크 에서 코드를 가져 와서 개체 루프 반복을 1 또는 2로 사용하여 코드를 실행합니다. 비 결정적 의미를 찾을 수 있습니다 (개체의 소멸자에 중단 점 설정). 정확히, Wait .. ()에 의해 1 개 (또는 2) 개의 느린 객체가있을 때 소멸자는 호출되지 않습니다. [인용 요구]
코드가 관리되지 않는 리소스 (예 : 외부 파일 핸들)를 처리하는 경우 소멸자 (또는 종료 자)를 구현해야합니다.
다음은 흥미로운 예입니다.
참고 : MSDN에서 위의 예제를 이미 시도한 경우 다음 코드가 공기를 제거합니다.
class Program
{
static void Main(string[] args)
{
SomePublisher publisher = new SomePublisher();
for (int i = 0; i < 10; i++)
{
SomeSubscriber subscriber = new SomeSubscriber(publisher);
subscriber = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(SomeSubscriber.Count.ToString());
Console.ReadLine();
}
}
public class SomePublisher
{
public event EventHandler SomeEvent;
}
public class SomeSubscriber
{
public static int Count;
public SomeSubscriber(SomePublisher publisher)
{
publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
}
~SomeSubscriber()
{
SomeSubscriber.Count++;
}
private void publisher_SomeEvent(object sender, EventArgs e)
{
// TODO: something
string stub = "";
}
}
먼저 출력이 무엇인지 분석 한 다음 실행 한 다음 아래 이유를 읽으십시오.
{소멸자는 프로그램이 종료 된 후에 만 암시 적으로 호출됩니다. } 객체를 결정적으로 정리하려면 IDisposable을 구현하고 Dispose ()를 명시 적으로 호출해야합니다. 그것이 본질입니다! :)
한 가지 더, GC Collect를 명시 적으로 트리거하면 프로그램 성능이 향상되지 않을 수 있습니다. 상황을 악화시킬 수 있습니다.
.NET GC는 적절하게 설계되고 조정되어 프로그램 메모리 사용량의 "습관"에 따라 GC0 / 1 / 2 임계 값을 조정할 수 있습니다. 따라서 일정 시간이 지나면 프로그램에 맞게 조정됩니다. GC.Collect를 명시 적으로 호출하면 임계 값이 재설정됩니다! 그리고 .NET은 프로그램의 "습관"에 다시 적응하는 데 시간을 투자해야합니다.
내 제안은 항상 .NET GC를 신뢰하는 것입니다. 모든 메모리 문제가 나타나면 ".NET Memory"성능 카운터를 확인하고 내 코드를 진단하십시오.
이것이 모범 사례인지 확실하지 않습니다 ...
제안 : 확실하지 않은 경우이를 구현하지 마십시오. 사실이 알려진시기를 재평가 한 다음 성능 테스트 전 / 후를 수행하여 확인합니다.
그러나 Collect () 호출이 부정적인 영향을 미치지 않는지 확인하기 위해 코드를 안정적으로 테스트 할 수 있다면 계속 진행하십시오.
IMHO, 이것은 "당신의 프로그램이 미래에 어떤 버그도 가지지 않을 것이라는 것을 증명할 수 있다면, 계속하십시오 ..." 라고 말하는 것과 비슷합니다 .
진지하게 GC를 강제하는 것은 디버깅 / 테스트 목적에 유용합니다. 다른 시간에해야한다고 느끼면 착각했거나 프로그램이 잘못 빌드 된 것입니다. 어느 쪽이든 솔루션은 GC를 강요하지 않습니다.