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


108

.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를 차지하는 것은 과도하게 보입니다.

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


14
왜 신경 쓰겠어요? 개인 작업 세트는 매우 낮습니다. 최신 OS는 메모리가 불필요한 경우 디스크로 페이지 아웃됩니다. 2009 년입니다. 임베디드 시스템에서 무언가를 구축하지 않는 한 10MB는 신경 쓰지 마십시오.
Mehrdad Afshari

8
.NET 사용을 중지하면 작은 프로그램을 사용할 수 있습니다. .NET 프레임 워크를로드하려면 많은 큰 DLL을 메모리에로드해야합니다.
Adam Sills

2
메모리 가격은 기하 급수적으로 떨어집니다 (예, 이제 24GB RAM이 장착 된 Dell 가정용 컴퓨터 시스템을 주문할 수 있습니다). 응용 프로그램이> 500MB를 사용하지 않는 한 최적화는 필요하지 않습니다.
알렉스

28
@LeakyCode 현대 프로그래머가 이런 식으로 생각하는 것이 정말 싫습니다. 애플리케이션의 메모리 사용에 관심을 가져야합니다. 대부분 Java 또는 C #으로 작성된 대부분의 최신 응용 프로그램은 리소스 관리와 관련하여 매우 비효율적이며 2014 년 덕분에 1998 년에 win95 및 64mb의 ram에서 가능한 한 많은 응용 프로그램을 실행할 수 있습니다. ... 단지 1 개의 브라우저 인스턴스가 이제 2GB의 램과 약 1GB의 간단한 IDE를 사용합니다. 램은 싸지 만 그렇다고해서 낭비해야하는 것은 아닙니다.
Petr

6
@Petr 자원 관리에 신경을 써야합니다. 프로그래머 시간도 리소스입니다. 10MB에서 2GB까지 낭비하지 마십시오.
Mehrdad Afshari 2014-06-04

답변:


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

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

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

그러나 여기에서 걱정하지 말라고 말하는 일부 사람들을 다루기 위해 : 10, 20 또는 그 이상의 사용자가 사용할 수있는 공유 서버에서 터미널 서비스 환경에서 실행될 Windows Forms 응용 프로그램을 작성하는 경우 예 , 반드시 메모리 사용량을 고려해야합니다. 그리고 경계해야합니다. 이 문제를 해결하는 가장 좋은 방법은 좋은 데이터 구조 설계를 사용하고 할당시기와 항목에 대한 모범 사례를 따르는 것입니다.


45

.NET 응용 프로그램은 프로세스에서 런타임과 응용 프로그램을 모두로드해야하므로 기본 응용 프로그램에 비해 풋 프린트가 더 큽니다. 정말 깔끔한 것을 원한다면 .NET이 최선의 선택이 아닐 수 있습니다.

그러나 응용 프로그램이 대부분 절전 모드 인 경우 필요한 메모리 페이지가 메모리에서 스왑되므로 대부분의 경우 시스템에 큰 부담이되지 않습니다.

설치 공간을 작게 유지하려면 메모리 사용량에 대해 생각해야합니다. 다음은 몇 가지 아이디어입니다.

  • 개체 수를 줄이고 필요한 것보다 오래 인스턴스를 유지하지 않도록합니다.
  • 주의 List<T>필요한 경우 용량을 두 배로 늘리면 최대 50 %의 낭비를 초래할 수있는 유사한 유형에 유의 .
  • 참조 유형보다 값 유형을 사용하여 스택에 더 많은 메모리를 강제 할 수 있지만 기본 스택 공간은 1MB에 불과합니다.
  • 85000 바이트를 초과하는 개체는 압축되지 않은 LOH로 이동하므로 쉽게 조각화 될 수 있으므로 피하십시오.

이것은 아마도 완전한 목록은 아니지만 몇 가지 아이디어 일뿐입니다.


IOW, 네이티브 코드 크기를 줄이기 위해 작동하는 동일한 종류의 기술이 .NET에서 작동합니까?
Robert Fraser

나는 약간의 겹침이 있다고 생각하지만 네이티브 코드를 사용하면 메모리 사용과 관련하여 더 많은 선택권이 있습니다.
Brian Rasmussen

17

이 경우 고려해야 할 한 가지는 CLR의 메모리 비용입니다. CLR은 모든 .Net 프로세스에 대해로드되므로 메모리 고려 사항을 고려합니다. 이러한 간단하고 작은 프로그램의 경우 CLR 비용이 메모리 공간을 지배 할 것입니다.

실제 애플리케이션을 구성하고이 기본 프로그램의 비용과 비교하여 비용을 보는 것이 훨씬 더 유익 할 것입니다.


7

구체적인 제안은 없지만 CLR Profiler (Microsoft에서 무료 다운로드)를 살펴볼 수 있습니다 .
설치가 완료되면이 방법 페이지를 살펴보십시오 .

방법에서 :

이 방법에서는 CLR 프로파일 러 도구를 사용하여 애플리케이션의 메모리 할당 프로필을 조사하는 방법을 보여줍니다. CLR 프로파일 러를 사용하여 메모리 누수 및 과도하거나 비효율적 인 가비지 수집과 같은 메모리 문제를 일으키는 코드를 식별 할 수 있습니다.


7

"실제"애플리케이션의 메모리 사용량을 살펴볼 수 있습니다.

Java와 유사하게 프로그램 크기에 관계없이 런타임에 고정 된 양의 오버 헤드가 있지만 그 이후에는 메모리 소비가 훨씬 더 합리적입니다.


4

이 간단한 프로그램의 개인 작업 세트를 줄이는 방법은 여전히 ​​있습니다.

  1. NGEN 귀하의 응용 프로그램. 이렇게하면 프로세스에서 JIT 컴파일 비용이 제거됩니다.

  2. MPGO를 사용하여 메모리 사용량 을 줄인 다음 NGEN을 사용 하여 애플리케이션을 교육 하십시오.


2

발자국을 줄이는 방법에는 여러 가지가 있습니다.

.NET 에서 항상 살아야 할 한 가지는 IL 코드의 네이티브 이미지 크기가 크다는 것입니다.

그리고이 코드는 애플리케이션 인스턴스간에 완전히 공유 될 수 없습니다. NGEN의 어셈블리 조차도 완전히 정적 인 것은 아니지만 JITting이 필요한 작은 부품이 여전히 있습니다.

사람들은 또한 필요한 것보다 훨씬 오래 메모리를 차단하는 코드를 작성하는 경향이 있습니다.

자주 볼 수있는 예 : Datareader를 사용하여 내용을 DataTable에로드하여 XML 파일에 기록합니다. OutOfMemoryException이 발생할 수 있습니다. OTOH, XmlTextWriter를 사용하고 Datareader를 스크롤하여 데이터베이스 커서를 스크롤 할 때 XmlNodes를 내보낼 수 있습니다. 이렇게하면 현재 데이터베이스 레코드와 해당 XML 출력 만 메모리에 있습니다. 더 높은 가비지 콜렉션 생성을 결코 얻지 못하거나 가능성이 낮으므로 재사용 할 수 있습니다.

일부 인스턴스의 목록을 가져오고, 일부 작업 (어딘가에서 참조 된 수천 개의 새 인스턴스가 생성됨)을 수행하고, 나중에 필요하지 않더라도 foreach가 끝날 때까지 모든 것을 참조하는 경우에도 마찬가지입니다. 입력 목록과 임시 부산물을 명시 적으로 무효화하면 루프를 종료하기 전에도이 메모리를 재사용 할 수 있습니다.

C #에는 반복 자라는 뛰어난 기능이 있습니다. 입력을 스크롤하여 객체를 스트리밍하고 다음 인스턴스를 얻을 때까지 현재 인스턴스 만 유지할 수 있습니다. LINQ를 사용하더라도 필터링을 원했다는 이유만으로 모든 정보를 유지할 필요는 없습니다.


1

특정 질문이 아닌 제목에있는 일반적인 질문에 답하기 :

많은 데이터를 반환하는 COM 구성 요소 (예 : double의 큰 2xN 배열)를 사용하고 있고 작은 부분 만 필요한 경우 .NET에서 메모리를 숨기고 해당 데이터 만 반환하는 래퍼 COM 구성 요소를 작성할 수 있습니다. 필요합니다.

이것이 제가 메인 애플리케이션에서 한 일이며 메모리 소비를 크게 향상 시켰습니다.


0

SetProcessWorkingSetSize 또는 EmptyWorkingSet API를 사용하여 장기 실행 프로세스에서 주기적으로 메모리 페이지를 디스크에 강제로 사용하면 컴퓨터가 재부팅 될 때까지 컴퓨터에서 사용 가능한 모든 실제 메모리가 효과적으로 사라질 수 있음을 발견했습니다. 메모리 집약적 인 작업을 수행 한 후 작업 집합을 줄이기 위해 EmptyWorkingSet API (SetProcessWorkingSetSize를 사용하는 대신)를 사용하는 네이티브 프로세스에 .NET DLL을로드했습니다. 1 일에서 1 주일 사이에 컴퓨터가 작업 관리자에서 실제 메모리 사용량을 99 %로 표시하는 반면, 상당한 메모리 사용량을 사용하는 프로세스는없는 것으로 나타났습니다. 곧 컴퓨터가 응답하지 않아 하드 재부팅이 필요합니다. 이 머신은 물리적 하드웨어와 가상 하드웨어 모두에서 실행되는 22 개 이상의 Windows Server 2008 R2 및 2012 R2 서버였습니다.

아마도 .NET 코드를 네이티브 프로세스에로드하는 것은 그것과 관련이 있지만, 자신의 책임하에 EmptyWorkingSet (또는 SetProcessWorkingSetSize)를 사용하십시오. 응용 프로그램을 처음 시작한 후 한 번만 사용하십시오. 코드를 비활성화하고 가비지 콜렉터가 자체적으로 메모리 사용량을 관리하도록하기로 결정했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.