program tip

.NET 애플리케이션의 메모리 사용량을 줄이십니까?

radiobox 2020. 8. 10. 07:54
반응형

.NET 애플리케이션의 메모리 사용량을 줄이십니까?


.NET 응용 프로그램의 메모리 사용량을 줄이는 몇 가지 팁은 무엇입니까? 다음과 같은 간단한 C # 프로그램을 고려하십시오.

class Program
{
    static void Main(string[] args)
    {
        Console.ReadLine();
    }
}

x64릴리스 모드 에서 컴파일되고 Visual Studio 외부에서 실행되는 작업 관리자는 다음을보고합니다.

Working Set:          9364k
Private Working Set:  2500k
Commit Size:         17480k

x86 전용으로 컴파일하면 조금 더 좋습니다 .

Working Set:          5888k
Private Working Set:  1280k
Commit Size:          7012k

그런 다음 동일한 작업을 수행하지만 런타임 초기화 후 프로세스 크기를 줄이려고하는 다음 프로그램을 시도했습니다.

class Program
{
    static void Main(string[] args)
    {
        minimizeMemory();
        Console.ReadLine();
    }

    private static void minimizeMemory()
    {
        GC.Collect(GC.MaxGeneration);
        GC.WaitForPendingFinalizers();
        SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
            (UIntPtr) 0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
    }

    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetProcessWorkingSetSize(IntPtr process,
        UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
}

Visual Studio 외부의 x86 릴리스 에 대한 결과 :

Working Set:          2300k
Private Working Set:   964k
Commit Size:          8408k

조금 더 좋지만 그런 간단한 프로그램에는 여전히 과도하게 보입니다. C # 프로세스를 좀 더 간결하게 만드는 트릭이 있습니까? 저는 대부분의 경우 백그라운드에서 실행되도록 설계된 프로그램을 작성하고 있습니다. 이미 별도의 응용 프로그램 도메인 에서 사용자 인터페이스 작업을 수행하고 있습니다. 즉, 사용자 인터페이스 항목을 안전하게 언로드 할 수 있지만 백그라운드에있을 때 10MB를 차지하는 것은 과도하게 보입니다.

추신 : 내가 관심을 갖는 이유에 대해 --- (파워) 사용자는 이러한 것들에 대해 걱정하는 경향이 있습니다. 성능에 거의 영향을 미치지 않더라도 기술에 정통한 사용자 (내 타겟 청중)는 백그라운드 애플리케이션 메모리 사용에 대해 쉿쉿하는 경향이 있습니다. Adobe Updater가 11MB의 메모리를 차지하는 것을보고 놀랐을 때조차도 놀랍게도 Foobar2000의 차분한 터치에 진정되는 느낌이 들었습니다. Foobar2000은 플레이 할 때도 6MB 미만을 차지할 수 있습니다. 나는 현대의 운영체제에서 이런 것들이 기술적으로 그다지 중요하지 않다는 것을 알고 있지만 그것이 인식에 영향을 미치지 않는다는 의미는 아닙니다.


  1. Stack Overflow 질문 .NET EXE 메모리 공간 을 확인하고 싶을 수도 있습니다 .
  2. MSDN 블로그 게시물 Working set! = 실제 메모리 풋 프린트 는 작업 세트, 프로세스 메모리 및 총 인 -RAM 소비량에 대한 정확한 계산을 수행하는 방법을 설명합니다.

응용 프로그램의 메모리 사용량을 무시해야한다고 말하지 않겠습니다. 분명히 더 작고 효율적인 것이 바람직한 경향이 있습니다. 그러나 실제 요구 사항이 무엇인지 고려해야합니다.

개인의 PC에서 실행되는 표준 Windows Forms 및 WPF 클라이언트 응용 프로그램을 작성하고 있으며 사용자가 작동하는 기본 응용 프로그램이 될 가능성이 높은 경우 메모리 할당에 대한 부족함을 피할 수 있습니다. (모든 것이 할당 해제되는 한)

However, to address some folks here who say not to worry about it: If you're writing a Windows Forms application which will be running in a terminal services environment, on a shared server possibly utilized by 10, 20 or more users, then yes, you absolutely must consider memory usage. And you will need to be vigilant. The best way to address this is with good data structure design and by following best practices regarding when and what you allocate.


.NET applications will have a bigger footprint compared to native applications due to the fact that they both have to load the runtime and the application in the process. If you want something really tidy, .NET may not be the best option.

However, keep in mind that if you application is mostly sleeping, the necessary memory pages will be swapped out of memory and thus not really be that much of a burden on the system at large most of the time.

If you want to keep the footprint small, you will have to think about memory usage. Here are a couple of ideas:

  • Reduce the number of objects and make sure not to hold on to any instance longer than required.
  • Be aware of List<T> and similar types that double capacity when needed as they may lead to up 50% waste.
  • You could consider using value types over reference types to force more memory on the stack, but keep in mind that the default stack space is just 1 MB.
  • Avoid objects of more than 85000 bytes, as they will go to LOH which is not compacted and thus may easily get fragmented.

That is probably not an exhaustive list by any means, but just a couple of ideas.


One thing you need to consider in this case is the memory cost of the CLR. The CLR is loaded for every .Net process and hence factors into the memory considerations. For such a simple / small program the cost of the CLR is going to dominate your memory footprint.

It would be much more instructive to construct a real application and view the cost of that compared to the cost of this baseline program.


No specific suggestions per se, but you might take a look at the CLR Profiler (free download from Microsoft).
Once you've installed it, take a look at this how-to page.

From the how-to:

This How To shows you how to use the CLR Profiler tool to investigate your application's memory allocation profile. You can use CLR Profiler to identify code that causes memory problems, such as memory leaks and excessive or inefficient garbage collection.


Might want to look at the memory usage of a "real" application.

Similar to Java there is some fixed amount of overhead for the runtime regardless of the program size, but memory consumption will be much more reasonable after that point.


There are still ways to reduce the private working set of this simple program:

  1. NGEN your application. This removes JIT compilation cost from your process.

  2. Train your application using MPGO reducing memory usage and then NGEN it.


There are many ways to reduce your footprint.

One thing you'll always have to live with in .NET is that the size of the native image of your IL code is huge

And this code cannot be completely shared between application instances. Even NGEN'ed assemblies are not completely static, they have still some little parts that need JITting.

People also tend to write code that blocks memory far longer than necessary.

An often seen example: Taking a Datareader, loading the contents into a DataTable just to write it into an XML file. You can easily run into an OutOfMemoryException. OTOH, you could use an XmlTextWriter and scroll through the Datareader, emitting XmlNodes as you scroll through the database cursor. That way, you only have the current database record and its XML output in memory. Which will never (or is unlikely to) get a higher garbage collection generation and thus can be reused.

The same applies to getting a list of some instances, doing some stuff (that spawns of thousands of new instances, which might stay referenced somewhere), and even though you don't need them afterwards, you still reference everything until after the foreach. Explicitly null-ing your input list and your temporary by-products means, this memory can be reused even before you exit your loop.

C# has an excellent feature called iterators. They allow you to stream objects by scrolling through your input and only keep the current instance until you get the next one. Even by using LINQ, you still don't need to keep all of it around just because you wanted it to be filtered.


Addressing the general question in the title and not the specific question:

If you are using a COM component that returns a lot of data (say large 2xN arrays of doubles) and only a small fraction is needed then one can write a wrapper COM component that hides the memory from .NET and returning only the data that is needed.

That is what I did in my main application and it significantly improved the memory consumption.


I have found that using the SetProcessWorkingSetSize or EmptyWorkingSet APIs to force memory pages to disk periodically in a long running process can result in all available physical memory on a machine to effectively disappear until the machine is rebooted. We had a .NET DLL loaded into a native process which would use the EmptyWorkingSet API (an alternative to using SetProcessWorkingSetSize) to reduce the working set after performing a memory intensive task. I found that after anywhere between 1 day to a week a machine would show 99% physical memory usage in Task Manager while no processes were shown to be using any significant memory usage. Soon after the machine would become unresponsive, requiring a hard reboot. Said machines were over 2 dozen Windows Server 2008 R2 and 2012 R2 servers running on both physical and virtual hardware.

Perhaps having the .NET code loaded into a native process had something to do with it, but use EmptyWorkingSet (or SetProcessWorkingSetSize) at your own risk. Perhaps only use it once after initial launch of your application. I have decided to disable the code and let the Garbage Collector manage memory usage on its own.

참고URL : https://stackoverflow.com/questions/1343374/reducing-memory-usage-of-net-applications

반응형