Хорошо, я думаю, что могу знать, что здесь происходит. Возьми это с щепоткой соли, хотя ...
Я немного изменил ваш код.
Xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="window" MouseDown="window_MouseDown">
</Window>
Код позади
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
private class HeavyObject : UserControl
{
private byte[] x = new byte[750000000];
public HeavyObject()
{
for (int i = 0; i < 750000000; i++ )
{
unchecked
{
x[i] = (byte)i;
}
}
// Release optimisation will not compile the array
// unless you initialize and use it.
Content = "Loadss a memory!! " + x[1] + x[1000];
}
}
const string msg = " ... nulled the HeavyObject ...";
public MainWindow()
{
InitializeComponent();
window.Content = msg;
}
private void window_MouseDown(object sender, MouseEventArgs e)
{
if (window.Content.Equals(msg))
{
window.Content = new HeavyObject();
}
else
{
window.Content = msg;
}
}
}
}
Теперь запустите это и нажмите на окно. Я изменил код на , не используя GC.Collect () и на установил память на mousedown . Я компилирую в Режим выпуска и запускаю без отладчика .
Медленно нажмите в окне .
При первом щелчке вы увидите, что использование памяти увеличилось на 750 МБ. Последующие щелчки переключают сообщение между .. Nulled the heavy object..
и Loads a memory!
, пока вы нажимаете медленно. Существует небольшая задержка, когда память выделяется и освобождается. Использование памяти не должно превышать 750 МБ, но оно также не уменьшается при обнулении тяжелого объекта. Это сделано специально, поскольку GC Large Object Heap является ленивым и собирает память больших объектов, когда требуется новая память больших объектов . Из MSDN:
Если у меня недостаточно свободного места для размещения запросов на выделение больших объектов, я сначала попытаюсь получить больше сегментов из ОС. Если это не удастся, я запусту сборку мусора 2-го поколения в надежде освободить место.
Быстро нажмите в окне
Около 20 кликов. Что просходит? На моем ПК использование памяти не увеличивается, но главное окно больше не обновляет сообщение. Это замерзает. Я не получаю исключение нехватки памяти, и общее использование памяти моей системы остается неизменным.
Подчеркните систему, используя MouseMove в окне
Теперь замените MouseDown для события MouseMove (согласно коду вашего вопроса), изменив этот Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="window" MouseMove="window_MouseDown">
</Window>
Я могу навести указатель мыши на него на некоторое время, но в конечном итоге выдает исключение OutofMemoryException . Хороший способ вызвать исключение - сделать несколько ходов, а затем попытаться максимизировать окно.
Теория
Хорошо, это моя теория. Поскольку ваш поток пользовательского интерфейса работает на полную мощность, создавая и удаляя память внутри события цикла сообщений (MouseMove), сборщик мусора не может идти в ногу. Сборка мусора в куче больших объектов выполняется , когда требуется память . Это также дорогая задача, поэтому она выполняется как можно реже.
Задержка, которую мы заметили при быстром нажатии на вкл / выкл / вкл / выкл, становится гораздо более выраженной при выполнении в MouseMove. Если будет выделен другой большой объект, в соответствии с этой статьей MSDN GC либо попытается собрать (если память недоступна), либо начнет использовать диск.
Система находится в состоянии нехватки памяти: это происходит, когда я получаю уведомление о высокой памяти из ОС. Если я думаю, что создание второго поколения GC будет продуктивным, я сработаю один.
Теперь эта часть интересна, и я думаю, что здесь, но
Наконец, на данный момент, LOH не уплотняется как часть коллекции, но это деталь реализации, на которую не следует полагаться. Поэтому, чтобы убедиться, что что-то не перемещено GC, всегда прикрепляйте это. Теперь возьмите свое новое знание LOH и возьмите под контроль кучу.
Итак, если куча больших объектов не сжимается и вы запрашиваете несколько больших объектов в тесном цикле, возможно ли, что коллекции приводят к фрагментации, в конечном итоге приводящей к исключению OutOfMemoryException, даже если теоретически должно быть достаточно памяти?
Решение
Давайте немного освободим поток пользовательского интерфейса, чтобы позволить комнате GC дышать. Оберните код выделения в асинхронный вызов Dispatcher и используйте низкий приоритет, такой как SystemIdle. Таким образом, только выделяет память, когда поток пользовательского интерфейса свободен.
private void window_MouseDown(object sender, MouseEventArgs e)
{
Dispatcher.BeginInvoke((ThreadStart)delegate()
{
if (window.Content.Equals(msg))
{
window.Content = new HeavyObject();
}
else
{
window.Content = msg;
}
}, DispatcherPriority.ApplicationIdle);
}
Используя это, я могу танцевать указатель мыши по всей форме , и он никогда не вызывает исключение OutOfMemoryException .Действительно ли это работает или "исправило" проблему, я не знаю, но это стоит проверить.
В заключение
- Обратите внимание, что объекты такого размера будут размещаться в куче больших объектов
- Выделение LOH вызывает GCесли недостаточно места
- LOH не сжимается во время цикла GC, поэтому может произойти фрагментация, приводящая к исключению OutOfMemoryException, несмотря на то, что теоретически достаточно памяти
- В идеале повторно использовать большие объекты и нет перераспределить.Если это невозможно:
- Allcate ваши крупные объекты в фоновом потоке, чтобы отделить от потока пользовательского интерфейса
- Чтобы позволить комнате GC дышать, используйте Dispatcher, чтобы отделить распределение до приложенияпростаивает