IT박스

C #에서 가비지 콜렉션 강제 실행을위한 우수 사례

itboxs 2020. 7. 21. 08:09
반응형

C #에서 가비지 콜렉션 강제 실행을위한 우수 사례


내 경험상 대부분의 사람들은 가비지 수집을 강제하는 것이 현명하지 않지만 0 세대에서 항상 수집되지는 않지만 메모리가 문제가되는 큰 객체로 작업하는 경우가 있다고 말합니다. 강제로 수집해도 괜찮습니까? 그렇게하는 가장 좋은 방법이 있습니까?


가장 좋은 방법은 가비지 수집을 강요하지 않는 것입니다.

MSDN에 따르면 :

"수집을 호출하여 가비지 수집을 강제 할 수 있지만 대부분 성능 문제가 발생할 수 있으므로 피해야합니다."

그러나 Collect () 호출이 부정적인 영향을 미치지 않는지 확인하기 위해 코드를 안정적으로 테스트 할 수 있다면 계속 진행하십시오 ...

더 이상 필요하지 않을 때 개체를 정리하십시오. 사용자 정의 개체가있는 경우 "using statement"및 IDisposable 인터페이스를 사용하십시오.

이 링크에는 메모리 / 가비지 수집 등을 해제하는 것과 관련하여 실용적인 조언이 있습니다.

http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx


모범 사례는 대부분의 경우 가비지 콜렉션을 강제 실행하지 않는 것입니다. (내가 작업 한 모든 시스템은 가비지 콜렉션을 강제 실행했으며, 해결 된 경우 가비지 콜렉션을 강제 실행해야 할 필요성을 제거하고 시스템을 크게 가속화시키는 문제를 강조했습니다.)

있습니다 몇 가지 경우 다음 가비지 컬렉터가하는 메모리 사용에 대해 더 알고있다. 다중 사용자 응용 프로그램이나 한 번에 하나 이상의 요청에 응답하는 서비스에서는 그렇지 않을 수 있습니다.

그러나 일부 배치 유형 처리 에서는 GC보다 더 많은 것을 알고 있습니다. 예를 들어 응용 프로그램을 고려하십시오.

  • 명령 행에 파일 이름 목록이 제공됩니다.
  • 단일 파일을 처리 한 후 결과 파일에 결과를 기록합니다.
  • 파일을 처리하는 동안 파일 처리가 완료 될 때까지 수집 할 수없는 많은 상호 연결된 개체를 만듭니다 (예 : 구문 분석 트리).
  • 처리 한 파일간에 많은 상태를 유지하지 않습니다 .

당신은 할 수 있습니다 각 파일을 처리 한 후에는 전체 가비지 컬렉션을 강제해야 테스트 (주의 후) 케이스를 만들 수.

또 다른 경우는 일부 항목을 처리하기 위해 몇 분마다 깨우는 서비스이며 잠자는 동안 상태를 유지하지 않는 서비스입니다 . 그럼 그냥 잠에 가기 전에 전체 수집을 강제 할 수 있습니다 보람.

컬렉션을 강제로 고려할 수있는 유일한 시간은 최근에 많은 객체가 생성되었고 현재 참조되는 객체가 거의 없다는 것을 알 때입니다.

나는 GC를 스스로 강요하지 않고이 유형의 것에 대한 힌트를 줄 수있을 때 가비지 수집 API를 사용하려고합니다.

" Rico Mariani의 성능 팁 "참조


이런 식으로보십시오-쓰레기통이 10 % 일 때 부엌 쓰레기를 버리는 것이 더 효율적입니까?

가득 채우지 않으면 쓰레기통 외부로 걸어가는 데 시간을 낭비하게됩니다. 이것은 GC 스레드가 실행될 때 발생하는 것과 유사합니다. 모든 관리되는 스레드는 실행되는 동안 일시 중단됩니다. 내가 실수하지 않으면 GC 스레드를 여러 AppDomain에서 공유 할 수 있으므로 가비지 수집이 모든 도메인에 영향을 미칩니다.

물론, 쓰레기통에 아무 것도 넣지 않는 상황이 생길 수 있습니다. 휴가를 가려고한다면 말입니다. 그런 다음 외출하기 전에 쓰레기를 버리는 것이 좋습니다.

프로그램이 유휴 상태이면 할당이 없기 때문에 사용중인 메모리가 가비지 수집되지 않습니다.


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()더 이상 참조되지 않는 객체는 정리합니다 .....


나는 당신이 이미 모범 사례를 열거했다고 생각하며 그것은 정말로 필요하지 않으면 그것을 사용하지 않는 것입니다. 이러한 질문에 먼저 답해야하는 경우 프로파일 링 도구를 사용하여 코드를 더 자세히 살펴 보는 것이 좋습니다.

  1. 코드에 필요한 것보다 더 넓은 범위의 항목을 선언하는 무언가가 있습니까?
  2. 메모리 사용량이 너무 높습니까?
  3. GC.Collect () 사용 전후의 성능을 비교하여 실제로 도움이되는지 확인하십시오.

프로그램에 메모리 누수가없고 객체가 누적되고 Gen 0에서 GC를 수행 할 수 없다고 가정 해 봅시다. 1) 오랫동안 참조되었으므로 Gen1 & Gen2로 들어가십시오. 2) 큰 객체 (> 80K)이므로 LOH (Large Object Heap)로 들어갑니다. LOH는 Gen0, Gen1 및 Gen2에서와 같이 압축을 수행하지 않습니다.

".NET 메모리"의 성능 카운터를 확인하면 1) 문제가 실제로 문제가 아님을 알 수 있습니다. 일반적으로 10 Gen0 GC마다 1 Gen1 GC가 트리거되고 10 Gen1 GC마다 1 Gen2 GC가 트리거됩니다. 이론적으로 GC0에 압력이 가해지지 않으면 (프로그램 메모리 사용량이 실제로 연결되어있는 경우) GC1 및 GC2를 절대 GC로 사용할 수 없습니다. 그것은 나에게 결코 일어나지 않습니다.

For problem 2), you can check ".NET Memory" performance counter to verify whether LOH is getting bloated. If it is really a issue to your problem, perhaps you can create a large-object-pool as this blog suggests http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx.


Large objects are allocated on LOH (large object heap), not on gen 0. If you're saying that they don't get garbage-collected with gen 0, you're right. I believe they are collected only when the full GC cycle (generations 0, 1 and 2) happens.

That being said, I believe on the other side GC will adjust and collect memory more aggressively when you work with large objects and the memory pressure is going up.

It is hard to say whether to collect or not and in which circumstances. I used to do GC.Collect() after disposing of dialog windows/forms with numerous controls etc. (because by the time the form and its controls end up in gen 2 due to creating many instances of business objects/loading much data - no large objects obviously), but actually didn't notice any positive or negative effects in the long term by doing so.


I would like to add that: Calling GC.Collect() (+ WaitForPendingFinalizers()) is one part of the story. As rightly mentioned by others, GC.COllect() is non-deterministic collection and is left to the discretion of the GC itself (CLR). Even if you add a call to WaitForPendingFinalizers, it may not be deterministic. Take the code from this msdn link and run the code with the object loop iteration as 1 or 2. You will find what non-deterministic means (set a break point in the object's destructor). Precisely, the destructor is not called when there were just 1 (or 2) lingering objects by Wait..().[Citation reqd.]

If your code is dealing with unmanaged resources (ex: external file handles), you must implement destructors (or finalizers).

Here is an interesting example:

Note: If you have already tried the above example from MSDN, the following code is going to clear the air.

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 = "";
        }
    }

I suggest, first analyze what the output could be and then run and then read the reason below:

{The destructor is only implicitly called once the program ends. } In order to deterministically clean the object, one must implement IDisposable and make an explicit call to Dispose(). That's the essence! :)


One more thing, triggering GC Collect explicitly may NOT improve your program's performance. It is quite possible to make it worse.

The .NET GC is well designed and tuned to be adaptive, which means it can adjust GC0/1/2 threshold according to the "habit" of your program memory usage. So, it will be adapted to your program after some time running. Once you invoke GC.Collect explicitly, the thresholds will be reset! And the .NET has to spent time to adapt to your program's "habit" again.

My suggestion is always trust .NET GC. Any memory problem surfaces, check ".NET Memory" performance counter and diagnose my own code.


Not sure if it is a best practice...

Suggestion: do not implement this or anything when unsure. Reevaluate when facts are known, then perform before/after performance tests to verify.


However, if you can reliably test your code to confirm that calling Collect() won't have a negative impact then go ahead...

IMHO, this is similar to saying "If you can prove that your program will never have any bugs in the future, then go ahead..."

In all seriousness, forcing the GC is useful for debugging/testing purposes. If you feel like you need to do it at any other times, then either you are mistaken, or your program has been built wrong. Either way, the solution is not forcing the GC...

참고URL : https://stackoverflow.com/questions/233596/best-practice-for-forcing-garbage-collection-in-c-sharp

반응형