IT story

언제 GC.Collect에 전화 할 수 있습니까?

hot-time 2020. 6. 4. 08:15
반응형

언제 GC.Collect에 전화 할 수 있습니까?


일반적인 조언은 GC.Collect코드에서 호출해서는 안되지만 이 규칙의 예외는 무엇입니까?

가비지 수집을 강제하는 것이 합리적 일 수있는 매우 구체적인 몇 가지 사례 만 생각할 수 있습니다.

마음에 떠오르는 한 가지 예는 서비스로, 간격을두고 일어나서 몇 가지 작업을 수행 한 다음 오랫동안 잠 들어 있습니다. 이 경우, 유휴 상태가 될 프로세스가 필요한 것보다 많은 메모리를 보유하지 못하도록 수집을 강제하는 것이 좋습니다.

전화 할 수있는 다른 경우가 GC.Collect있습니까?


중요한 개체 집합, 특히 1 세대와 2 세대에있는 것으로 의심되는 개체가 이제 가비지 수집 대상이되고 작은 성능 저하라는 측면에서 수집하기에 적절한시기가되었다고 믿을만한 충분한 이유가있는 경우 .

좋은 예는 방금 큰 양식을 닫은 경우입니다. 이제 모든 UI 컨트롤을 가비지 수집 할 수 있으며 양식을 닫을 때 아주 짧은 일시 중지는 사용자에게 눈에 띄지 않을 것입니다.

업데이트 2.7.2018

.NET 4.5으로 -이 GCLatencyMode.LowLatencyGCLatencyMode.SustainedLowLatency. 이 모드 중 하나를 시작하거나 종료 할 때는을 사용하여 전체 GC를 강제 실행하는 것이 좋습니다 GC.Collect(2, GCCollectionMode.Forced).

.NET 4.6부터는 GC.TryStartNoGCRegion(읽기 전용 값을 설정하는 데 사용되는 GCLatencyMode.NoGCRegion) 방법이 있습니다. 이것은 충분한 메모리를 확보하기 위해 전체 차단 가비지 수집을 수행 할 수 있지만 일정 기간 동안 GC를 허용하지 않는다면 전후에 전체 GC를 수행하는 것이 좋습니다.

출처 : Microsoft 엔지니어 Ben Watson : 고성능 .NET 코드 작성 , 2 차 Ed. 2018.

보다:


나는 GC.Collect조잡한 성능 / 프로파일 러 테스트 리그를 작성할 때만 사용 합니다. 즉, 테스트 할 코드 블록이 두 개 이상 있습니다.

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestA(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestB(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
...

그래서 TestA()TestB()가능한 한 유사한 상태로 실행 - 즉 TestB()때문에 단지 망치되지 않습니다 TestA매우 가까운 티핑 포인트를 떠났다.

전형적인 예는 간단한 콘솔 exe ( Main예를 들어 여기에 게시 될 수 있는 방법)인데, 이는 문자열 연결과와의 차이점을 보여줍니다 StringBuilder.

내가 정확한 것을 필요로한다면, 이것은 완전히 독립적 인 두 가지 테스트 일 것입니다.하지만 종종 테스트 중에 GC를 최소화 (또는 정규화)하여 행동에 대한 거친 느낌을 얻으려면 충분합니다.

생산 코드 중에? 나는 아직 그것을 사용하지 않았다 ;-p


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

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

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

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

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

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

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

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

" Rico Mariani의 성능 팁 "참조


한 가지 경우는 WeakReference 를 사용하는 코드를 단위 테스트하려고 할 때 입니다.


메시지, RPC 요청에 반응하거나 데이터베이스 또는 프로세스를 지속적으로 폴링하는 24/7 또는 24/6 시스템의 경우 메모리 누수를 식별하는 방법이 유용합니다. 이를 위해 처리를 일시적으로 일시 중단하고 전체 가비지 수집을 수행하는 메커니즘을 응용 프로그램에 추가하는 경향이 있습니다. 이렇게하면 시스템이 남아있는 메모리가 합법적으로 오래 사용 된 메모리 (캐시, 구성 등)이거나 '누설 된'(루트가 예상되지 않았지만 실제로는 근본 인 객체) 상태 인 대기 상태가됩니다.

이 메커니즘을 사용하면 활성 처리의 노이즈로 보고서가 흐리게 표시되지 않으므로 메모리 사용량을 훨씬 쉽게 프로파일 링 할 수 있습니다.

모든 가비지를 확보하려면 두 가지 콜렉션을 수행해야합니다.

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

첫 번째 컬렉션은 종료자를 가진 모든 객체를 마무리하지만 실제로는 이러한 객체를 가비지 수집하지 않습니다. 두 번째 GC는 이러한 최종 객체를 가비지 수집합니다.


가비지 수집기에서 제공하지 않는 앱의 특성에 대해 알고있는 경우 GC.Collect ()를 호출 할 수 있습니다. 저자로서 이것이 가능성이 높다고 생각하고 싶어합니다. 그러나 진실은 GC가 꽤 잘 작성되고 테스트 된 전문가 시스템에 해당한다는 것이며, 저수준 코드 경로에 대해 알지 못하는 경우는 거의 없습니다.

추가 정보가있는 위치를 생각할 수있는 가장 좋은 예는 유휴 기간과 매우 바쁜 기간 사이를 순환하는 앱입니다. 사용량이 많은 기간 동안 가능한 최고의 성능을 원하므로 유휴 시간을 사용하여 정리를 수행하려고합니다.

그러나 대부분의 경우 GC는 어쨌든 이것을 할만 큼 똑똑합니다.


메모리 조각화 솔루션으로. 많은 데이터를 메모리 스트림에 쓰는 동안 (네트워크 스트림에서 읽음) 메모리 예외가 발생했습니다. 데이터는 8K 청크로 작성되었습니다. 128M에 도달 한 후 사용 가능한 메모리가 많지만 예외가 발생했습니다 (그러나 조각화 됨). GC.Collect ()를 호출하면 문제가 해결되었습니다. 수정 후 1G 이상을 처리 할 수있었습니다.


Rico Mariani의이 기사를 살펴보십시오. 그는 GC.Collect를 호출 할 때 두 가지 규칙을 제공합니다 (규칙 1은 "안함").

GC.Collect ()를 호출 할 때


GC.Collect () 호출이 거의 필요한 경우는 Interop을 통해 Microsoft Office를 자동화 할 때입니다. Office 용 COM 개체는 자동으로 해제되는 것을 좋아하지 않으므로 Office 제품 인스턴스가 매우 많은 양의 메모리를 차지할 수 있습니다. 이것이 문제인지 또는 의도적으로인지 확실하지 않습니다. 인터넷 주위 에이 주제에 대한 많은 게시물이 있으므로 너무 자세하게 다루지 않습니다.

Interop를 사용하여 프로그래밍 할 때는 일반적으로 Marshal.ReleseComObject ()를 사용하지만 모든 단일 COM 개체를 수동으로 해제해야합니다. 또한 가비지 콜렉션을 수동으로 호출하면 "정리"하는 데 도움이됩니다. Interop 개체를 다룰 때 다음 코드를 호출하면 상당히 도움이 될 것 같습니다.

GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()

개인적으로 ReleaseComObject와 수동으로 가비지 수집을 함께 사용하면 Office 제품, 특히 Excel의 메모리 사용량이 크게 줄어 듭니다.


배열 및 목록에서 성능 테스트를 수행했습니다.

private static int count = 100000000;
private static List<int> GetSomeNumbers_List_int()
{
    var lstNumbers = new List<int>();
    for(var i = 1; i <= count; i++)
    {
        lstNumbers.Add(i);
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Array()
{
    var lstNumbers = new int[count];
    for (var i = 1; i <= count; i++)
    {
        lstNumbers[i-1] = i + 1;
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Enumerable_Range()
{
    return  Enumerable.Range(1, count).ToArray();
}

static void performance_100_Million()
{
    var sw = new Stopwatch();

    sw.Start();
    var numbers1 = GetSomeNumbers_List_int();
    sw.Stop();
    //numbers1 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"List<int>\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
    var numbers2 = GetSomeNumbers_Array();
    sw.Stop();
    //numbers2 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"int[]\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
//getting System.OutOfMemoryException in GetSomeNumbers_Enumerable_Range method
    var numbers3 = GetSomeNumbers_Enumerable_Range();
    sw.Stop();
    //numbers3 = null;
    //GC.Collect();

    Console.WriteLine(String.Format("\"int[]\" Enumerable.Range took {0} milliseconds", sw.ElapsedMilliseconds));
}

내가 가지고 OutOfMemoryException있는 유일한 해결 방법으로 메모리 할당을 해제하는 것입니다 GetSomeNumbers_Enumerable_Range의 방법 :

numbers = null;
GC.Collect();

귀하의 예에서 GC.Collect를 호출하는 것은 문제가 아니라 디자인 문제가 있다고 생각합니다.

일정한 간격으로 (설정된 시간) 깨우려면 프로그램을 한 번만 실행하고 (작업을 한 번 수행) 종료해야합니다. 그런 다음 프로그램을 예약 된 간격으로 실행되도록 예약 된 작업으로 설정합니다.

This way, you don't have to concern yourself with calling GC.Collect, (which you should rarely if ever, have to do).

That being said, Rico Mariani has a great blog post on this subject, which can be found here:

http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx


One useful place to call GC.Collect() is in a unit test when you want to verify that you are not creating a memory leak (e. g. if you are doing something with WeakReferences or ConditionalWeakTable, dynamically generated code, etc).

For example, I have a few tests like:

WeakReference w = CodeThatShouldNotMemoryLeak();
Assert.IsTrue(w.IsAlive);
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsFalse(w.IsAlive);

It could be argued that using WeakReferences is a problem in and of itself, but it seems that if you are creating a system that relies on such behavior then calling GC.Collect() is a good way to verify such code.


The short answer is: never!


using(var stream = new MemoryStream())
{
   bitmap.Save(stream, ImageFormat.Png);
   techObject.Last().Image = Image.FromStream(stream);
   bitmap.Dispose();

   // Without this code, I had an OutOfMemory exception.
   GC.Collect();
   GC.WaitForPendingFinalizers();
   //
}

There are some situations where it is better safe than sorry.

Here is one situation.

It is possible to author an unmanaged DLL in C# using IL rewrites (because there are situations where this is necessary).

Now suppose, for example, the DLL creates an array of bytes at the class level - because many of the exported functions need access to such. What happens when the DLL is unloaded? Is the garbage collector automatically called at that point? I don't know, but being an unmanaged DLL it is entirely possible the GC isn't called. And it would be a big problem if it wasn't called. When the DLL is unloaded so too would be the garbage collector - so who is going to be responsible for collecting any possible garbage and how would they do it? Better to employ C#'s garbage collector. Have a cleanup function (available to the DLL client) where the class level variables are set to null and the garbage collector called.

Better safe than sorry.


i am still pretty unsure about this. I am working since 7 years on an Application Server. Our bigger installations take use of 24 GB Ram. Its hightly Multithreaded, and ALL calls for GC.Collect() ran into really terrible performance issues.

Many third party Components used GC.Collect() when they thought it was clever to do this right now. So a simple bunch of Excel-Reports blocked the App Server for all threads several times a minute.

We had to refactor all the 3rd Party Components in order to remove the GC.Collect() calls, and all worked fine after doing this.

But i am running Servers on Win32 as well, and here i started to take heavy use of GC.Collect() after getting a OutOfMemoryException.

But i am also pretty unsure about this, because i often noticed, when i get a OOM on 32 Bit, and i retry to run the same Operation again, without calling GC.Collect(), it just worked fine.

One thing i wonder is the OOM Exception itself... If i would have written the .Net Framework, and i can't alloc a memory block, i would use GC.Collect(), defrag memory (??), try again, and if i still cant find a free memory block, then i would throw the OOM-Exception.

Or at least make this behavior as configurable option, due the drawbacks of the performance issue with GC.Collect.

Now i have lots of code like this in my app to "solve" the problem:

public static TResult ExecuteOOMAware<T1, T2, TResult>(Func<T1,T2 ,TResult> func, T1 a1, T2 a2)
{

    int oomCounter = 0;
    int maxOOMRetries = 10;
    do
    {
        try
        {
            return func(a1, a2);
        }
        catch (OutOfMemoryException)
        {
            oomCounter++;
            if (maxOOMRetries > 10)
            {
                throw;
            }
            else
            {
                Log.Info("OutOfMemory-Exception caught, Trying to fix. Counter: " + oomCounter.ToString());
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(oomCounter * 10));
                GC.Collect();
            }
        }
    } while (oomCounter < maxOOMRetries);

    // never gets hitted.
    return default(TResult);
}

(Note that the Thread.Sleep() behavior is a really App apecific behavior, because we are running a ORM Caching Service, and the service takes some time to release all the cached objects, if RAM exceeds some predefined values. so it waits a few seconds the first time, and has increased waiting time each occurence of OOM.)


You should try to avoid using GC.Collect() since its very expensive. Here is an example:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet.Remove(RecordSet[0]);

        }
        GC.Collect(); // AVOID
    }

TEST RESULT: CPU USAGE 12%

When you change to this:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet[0].Dispose(); //  Bitmap destroyed!
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet[0].Dispose(); //  Bitmap destroyed!
            RecordSet.Remove(RecordSet[0]);

        }
        //GC.Collect();
    }

TEST RESULT: CPU USAGE 2-3%


Scott Holden's blog entry on when to (and when not to) call GC.Collect is specific to the .NET Compact Framework, but the rules generally apply to all managed development.


This isn't that relevant to the question, but for XSLT transforms in .NET (XSLCompiledTranform) then you might have no choice. Another candidate is the MSHTML control.


If you are using a version of .net less than 4.5, manual collection may be inevitable (especially if you are dealing with many 'large objects').

this link describes why:

https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/


one good reason for calling GC is on small ARM computers with little memory, like the Raspberry PI (running with mono). If unallocated memory fragments use too much of the system RAM, then the Linux OS can get unstable. I have an application where I have to call GC every second (!) to get rid of memory overflow problems.

Another good solution is to dispose objects when they are no longer needed. Unfortunately this is not so easy in many cases.


Since there are Small object heap(SOH) and Large object heap(LOH)

We can call GC.Collect() to clear de-reference object in SOP, and move lived object to next generation.

In .net4.5, we can also compact LOH by using largeobjectheapcompactionmode

참고URL : https://stackoverflow.com/questions/478167/when-is-it-acceptable-to-call-gc-collect

반응형