Изучая .NET Framework я решил написать простое приложение-украшательство в виде аналоговых часов на рабочий стол, и так, как предполагалось, что программа будет работать все время, пока загружена Windows было важно, чтобы она использовала как можно меньше системных ресурсов. Я открыл свою любимую Visual Studio 2008, скомпилировал и запустил пустой проект WinForms и что вы думаете я увидел в диспетчере задач? 20 мегабайт оперативной памяти занимала моя программа, и это без полезной нагрузки! Ну не фига себе пельмень, подумал я и начал искать решение.

Откуда такой аппетит.

Немного погуглив я выяснил, что на самом деле, это не память, фактически используемая приложением, а так называемое «рабочее множество». Если не вникать в теорию операционных систем, это память, зарезервированная процессом про запас. Как правило, большинство приложений не использует все свое рабочее множество, оно устанавливается операционной системой при запуске, причем практически «на глаз», а если приложение требует больше памяти, чем было выделено при запуске, то, рабочее множество автоматически расширяется. Расширение рабочего множества – операция не быстрая, поэтому лучше оставлять его с запасом, что операционная система собственно и делает.

Изучение методик похудения.

Если вы все-таки хотите чтобы ваша программа была «незаметна» в памяти, есть возможность немного подрезать свое рабочее пространство, прибегнув к Win32 API функции SetProcessWorkingSetSize. Первым параметрам данной функции передается хендл процесса, а вторым и третьим соответственно минимальный и максимальный размер рабочего пространства. Ну, вроде бы все ясно, остался только один не решенный вопрос, как же рассчитать необходимый размер рабочего пространства, тут оказалось все просто нужно передать вторым и третьим параметром -1 и Windows сама рассчитает минимальный размер памяти необходимый нашему приложению.

От слов к делу. Худеем вместе.

Объявление API функции в VisualBasic.NET:

Код: VB
1
2
3
4
5
Public Class NativeMethods
    <DllImport("kernel32.dll")>_
    Public Shared Function SetProcessWorkingSetSize(ByVal handle As IntPtr, ByVal minimumWorkingSetSize As Integer, ByVal maximumWorkingSetSize As Integer) As Boolean
    End Function
End Class

Вызов функции:

Код: VB
1
2
3
Private Sub Form_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    NativeMethods.SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1)
End Sub

Компилируем, запускаем, смотрим в диспетчере задач (Ctrl+Shift+Esc) или (Ctrl+Alt+Del), о чудо программа занимает всего 400 килобайт с хвостиком. Но радость наша не долга, память опять начинает упорно расти. Что делать? Не в таймере, же вызывать функцию, это очень не красивое решение. Чтобы обуздать ненасытную программу я предлагаю вот такое решение:

Код: VB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Private Sub Form_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    'Когда приложение будет переходить в состояние простоя
    'будем выполнять сборку мусора и освобождение неиспользуемой памяти
    AddHandler Application.Idle, AddressOf FlushMemory
End Sub
 
Private Sub FlushMemory()
    'Сборка мусора
    GC.Collect()
    GC.WaitForPendingFinalizers()
    'Освобождаем неиспользуемую память
    If System.Environment.OSVersion.Platform = PlatformID.Win32NT Then
        NativeMethods.SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1)
    End If
End Sub

Компилируем, запускаем, смотрим. Теперь все в порядке.