Отказ от ответственности:
У меня огромная проблема с управлением памятью в моем приложении.Они появились впервые в мае 2018 года, но благодаря различным статьям на MSDN и здесь, SO, я смог уменьшить проблему, но не смог ее решить.После того, как я немного прочел, протестировал и написал код, я пришел сюда, потому что мне нужна помощь.
Фон приложения:
Мое приложение - приложение для управления продажами, для маленького клиента.Он работает на удаленных рабочих столах с довольно ограниченным объемом памяти, поэтому мне нужно сохранять низкое использование памяти.MainWindow, состоит из меню списка, справа (old-schoolish), после нажатия на элемент меню, новая Страница открывается как RadTabItem (ib4 вы ударили меня «Спросите teleriks»для этого ", это не имеет значения, у меня та же проблема с содержимым TabItems), вот пример кода:
private void KlientDzwoniTab(object sender, MouseButtonEventArgs e)
{
App.StronaGlowna.Cursor = Cursors.Wait;
try
{
if (CheckIfTabIsOpened("Klient dzwoni") == false)
{
_nazwakarty = "Klient dzwoni";
var strona = new KlientDzwoni();
AddItem(strona, _nazwakarty);
App.StronaGlowna.Cursor = Cursors.Arrow;
}
else
{
SelectTab("Klient dzwoni");
App.StronaGlowna.Cursor = Cursors.Arrow;
}
}
catch (Exception exception)
... catch block and stuff
и важная вещь, которая является функцией AddItem ():
public void AddItem(Page strona, string header)
{
try
{
#region Close button region
var grid = new Grid();
var c1 = new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) };
var c2 = new ColumnDefinition { Width = new GridLength(21) };
grid.ColumnDefinitions.Add(c1);
grid.ColumnDefinitions.Add(c2);
var button = new RadButton
{
Padding = new Thickness(0, 0, 0, 0),
Margin = new Thickness(5, 0, 0, 0),
Width = 16,
Height = 16,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Top,
VerticalContentAlignment = VerticalAlignment.Top,
Content = "x",
FontWeight = FontWeights.Bold,
Background = Brushes.Transparent,
BorderThickness = new Thickness(0, 0, 0, 0),
Tag = header
};
button.Click += Zamknijtab;
var stringHeader = header.Replace(" ", "");
stringHeader = stringHeader.Replace(":", "");
stringHeader = stringHeader.Replace("/", "");
stringHeader = stringHeader.Replace("-", "");
stringHeader = stringHeader.Replace(".", "");
stringHeader = stringHeader.Replace("_", "");
var textBlock = new TextBlock
{
Text = header
};
// panel.Name = header;
grid.Children.Add(textBlock);
Grid.SetColumn(textBlock, 0);
grid.Children.Add(button);
Grid.SetColumn(button, 1);
#endregion
var content = strona.Content;
var itemToAdd = new RadTabItem
{
Header = grid,
Content = content
};
var klientDzwoni = strona as KlientDzwoni;
if (klientDzwoni != null)
{
itemToAdd.KeyDown += klientDzwoni.F3KeyDown;
}
itemToAdd.SetValue(NameProperty, stringHeader);
tabControl.Items.Add(itemToAdd);
tabControl.SelectedItem = itemToAdd;
}
catch (Exception e)
{
...catch stuff
}
}
Теперь пользователь может, очевидно, закрыть эти вкладки, нажав на событие Zamknijtab:
public void Zamknijtab(object sender, RoutedEventArgs e)
{
try
{
var listaTabow = tabControl.Items;
var button = sender as RadButton;
if (button != null)
{
if (button.Tag != null)
{
var stringHeader = button.Tag as string;
stringHeader = stringHeader.Replace(" ", "");
stringHeader = stringHeader.Replace(":", "");
stringHeader = stringHeader.Replace("/", "");
stringHeader = stringHeader.Replace("-", "");
stringHeader = stringHeader.Replace(".", "");
stringHeader = stringHeader.Replace("_", "");
var tabs = listaTabow.Cast<RadTabItem>();
tab = tabs.Reverse().FirstOrDefault(f => f.Name == stringHeader);
if (tab == null)
{
tab = tabs.Reverse().FirstOrDefault(f => f.Name == stringHeader);
}
if (tab != null)
{
if (button.Tag.ToString().Contains("Zam:") &&
!button.Tag.ToString().Contains("podsumowanie"))
{
NumerZamowienia = stringHeader.Replace("Zam", "");
if (App.ZamowieniaCommitGet(NumerZamowienia))
{
var textBox = new Label
{
Content =
"Na zamówieniu są wprowadzone pozycje, czy chcesz usunąć zamówienie?!",
FontWeight = FontWeights.Bold,
Foreground = Brushes.Red
};
RadWindow.Confirm(new DialogParameters
{
Header = "Potwierdź zamknięcie okna",
Content = textBox,
Closed = OnConfirmClosed,
Owner = App.StronaGlowna,
OkButtonContent = "Tak",
CancelButtonContent = "Nie"
});
}
else
{
if (NumerZamowienia != null)
{
var zamowienie = Db.dst_Orders.FirstOrDefault(f => f.Numer == NumerZamowienia);
if (zamowienie != null)
{
ZamowienieId = zamowienie.Id_Order;
}
var pozycje = Db.dst_OrderLines.Where(f =>
f.Order_Id == ZamowienieId && f.Ilosc != null && f.Ilosc > 0);
if (!pozycje.Any())
{
var id = App.GetUserId();
Db.No_Order(ZamowienieId, null, id);
}
tabControl.Items.Remove(tab);
}
}
}
else
{
tabControl.Items.Remove(tab);
}
}
}
else
{
var typ = tabControl.SelectedItem as RadTabItem;
var tabs = listaTabow.Cast<RadTabItem>();
if (typ != null)
{
tab = tabs.Reverse().FirstOrDefault(f => f.Name == typ.Name);
if (tab != null)
{
tabControl.Items.Remove(tab);
}
}
}
}
else
{
var typ = tabControl.SelectedItem as RadTabItem;
var tabs = listaTabow.Cast<RadTabItem>();
if (typ != null)
{
tab = tabs.Reverse().FirstOrDefault(f => f.Name == typ.Name);
if (tab != null)
{
tabControl.Items.Remove(tab);
}
}
}
}
catch (Exception exception)
{
... Another catch
Этот, возможно, был бы немного переработан, но это так, как есть.
Проблема:
Закрытие вкладок, не освобождает выделенную им память (как никогда).
ВсеМои элементы, открытые в RadTabItems, являются пользовательскими Pages (пробовал с UserControl , та же проблема), и проблема существует для всех них.Класс KlientDzwoni()
- это маленький класс, увеличивающий использование памяти примерно на 4 МБ, но у меня гораздо большие страницы, которые увеличивают его на 100-200 МБ (средства отчетности).
Говоря о памяти и МБ, вы можете спросить, как я их проанализировал, поэтому я начал с ofc, диспетчера задач, и он показывает, что использование памяти увеличивается, каждый раз, когда я открываю новую вкладку, и никогдаидет вниз.Прочитав этот замечательный пост , я скачал JetBrains dotMemory, и он помог мне обнаружить утечки памяти в моем приложении, НО на самом деле это не вызывает не освобождение памяти как утечку ,
По моему мнению, эти страницы (например, KlientDzwoni
) по какой-то причине хранятся где-то в памяти и никогда не освобождаются И НЕ рассматриваются как ненужные (таким образом, не обнаруживаются как утечка).Вот вещи, которые я пытался исправить, чтобы решить эту проблему.
Мои жалкие попытки:
- Ссылаясь на статью, упомянутую ранее, я пыталсяиспользуйте
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
.На самом деле это немного помогает (уменьшает утечку на ~ 10-20%), но не решает проблему полностью.Кроме того, так как не рекомендуется заставлять GC работать, я немного напуган. - BindingOperations.ClearBinding - я думал, что эта идея довольно крутая.Я имею в виду, что я не отменяю привязку событий и свойств на своих страницах, во-первых, потому что у меня нет правильного события для этого (ну, нет события закрытия, есть Unloaded с другой стороны), а во-вторых, это C #поэтому я ожидаю, что Dispatcher и GC сделают эту работу за меня.Может быть, я совершенно не прав по этому поводу, и я должен отписаться от всех событий и аннулировать все коллекции и переменные в каком-либо событии, но когда TabItems удаляется, его содержимое (Page.Content) удаляется, поэтому не должнобыть любым событием, отменить подписку, верно?
- Обнулять переменные и свойства, такие как TabItem.Content, переменные, объекты страницы и т. д. 0 результатов.
- Использование UserControl Я упоминал выше, это совсем не помогает.
- Отказ от моего любимого фреймворка, которым является Telerik (это не так), и использование TabItems вместо RadTabItems, 0 результатов.
Что было на самом деле бесполезно:
Есть много постов, статей, тезисов, о памяти Утечки (Я все еще не уверенесли это утечка) и проблемы TabControl, но, я думаю, они плохо сконструированы, поэтому ответы неточны по нескольким причинам:
- Многие ответы сосредоточены на «утечке памяти», с идеями, как проверить, является ли это утечкой памяти, или нет, или это, я не знаю, абрикос.Суть этой проблемы в том, что память выделяется и никогда не освобождается.Мне все равно, как мы называем эту проблему.
- Другие ответы связывают вопросы / сообщения / статьи / ответы с одинаковыми, повторяющимися вопросами. Чаще всего эта ссылка используется .
Мне не удалось найти реальное решение этой проблемы.Он был опубликован на SO, MSDN и в других местах, много раз, за последние несколько лет, но я так и не нашел OP-ответ типа "спасибо, ребята, проблема была в xxx, исправление было в yyy".Но есть какая-то закономерность: кто-то приходит с проблемами с памятью TabItem и никогда не уходит с правильным ответом.
Итак, кто-то может, наконец, ответить на вопрос «Как освободить выделенную память» и решить эту бесконечную проблему?
РЕДАКТИРОВАТЬ 2:
Загруженный пример проекта здесь:
https://1drv.ms/u/s!Ali8Cn1kITEDhEwn-WAEEl04talS Он состоит из компонентов telerik, но может быть заменен на vanilla WPF для представления того же поведения.,