I'm dealing with a memory leak in my code. I'm using C# and WPF
Let me explain the code a bit. Firstly, I'm reading a long text file (several thousands of lines) into a string array. After that, im going through each line in the array, and I'm filtering out the lines I don't need. After a bunch of filtering, around 3000 lines are remaining. Then, I create a new object of the type ItemEntry for each line that remained. An ItemEntry object has a canvas, that contains several textboxes, textblocks and buttons. When that is done, all the 3000 objects are listed in a StackPanel that is inside a scrollviewer.
When reading a file, it always goes through this process. Before reading a new file, it clears the StackPanel. One process takes up a lot of memory already, but unfortunately, upon cleaning the StackPanel and reading a new file of the same length but with different content, it does not seem to free up the memory used by the content of the StackPanel before, even though I cleared it. The usage is only rising.
I'd appreciate help finding/fixing the memory leak, as well as ideas for improving the memory consumption in the first place. Thank you very much!
Here is the code that reads the file and displays it:
public async void LoadLootTable(string path)
{
//Get list of content in file, remove all non-item lines so only items remain
string[] loadedItems = File.ReadAllLines(currentLootTable);
List<string> items = new List<string>();
items.Clear();
foreach (string item in loadedItems)
{
if (item.Contains("\"tag\""))
{
string itemFiltered;
itemFiltered = item.Replace(" ", "");
itemFiltered = itemFiltered.Replace("\"tag\":", "");
itemFiltered = itemFiltered.Substring(1, itemFiltered.Length - 2);
items.Add(itemFiltered);
}
else if (!item.Contains("\"tag\"") && !item.Contains("{") && !item.Contains("}") && !item.Contains("[") && !item.Contains("]") && !item.Contains("\"rolls\"") && !item.Contains("\"type\"") && !item.Contains("\"function\"") && item.Contains("\"") && !item.Contains("\"weight\"") && !item.Contains("\"count\"") && !item.Contains("\"min\": 1") && !item.Contains("\"max\": 64") && !item.Contains("\"out\"") && !item.Contains("\"score\""))
{
string itemFiltered;
itemFiltered = item.Replace("\"", "");
itemFiltered = itemFiltered.Replace("name:", "");
itemFiltered = itemFiltered.Replace(" ", "");
itemFiltered = itemFiltered.Replace("tag:", "");
itemFiltered = itemFiltered.Replace(",", "");
items.Add(itemFiltered);
}
}
List<string> finalItemList = new List<string>();
//Check for each item if it has NBT and add it to the string
for (int i = 0; i < items.Count; i++)
{
if (!items[i].Contains("{"))
{
if (i < items.Count - 1)
{
if (items[i + 1].Contains("{"))
{
finalItemList.Add(string.Format("{0};{1}", items[i], items[i + 1]));
}
if (!items[i + 1].Contains("{"))
{
finalItemList.Add(string.Format("{0};none", items[i]));
}
}
else
{
{
finalItemList.Add(string.Format("{0};none", items[i]));
}
}
}
}
itemList.Clear();
int index = 0;
//Add an entry for all items
foreach (string item in finalItemList)
{
string[] splitItem = item.Split(';');
itemList.Add(new itemEntry(splitItem[0], splitItem[1], index));
index++;
}
//Show 'loading' message
stpWorkspace.Children.Clear();
tblLoadingItems.Margin = new Thickness(svWorkspace.ActualWidth / 2 - 150, svWorkspace.ActualHeight / 2 - 75, 0, 0);
tblLoadingItems.Text = "Loading items, please wait...\nThis may take a few seconds!";
stpWorkspace.Children.Add(tblLoadingItems);
await Task.Delay(5); //Allows the UI to update and show the textblock
//Add all item entrys to workspace
stpWorkspace.Children.Clear();
tblLootTableStats.Text = string.Format("Current Loot table: {0} - Total amount of items: {1}", currentLootTable.Replace(currentDatapack, "").Replace("/data/randomitemgiver/loot_tables/", ""), itemList.Count);
stpWorkspace.Children.Add(cvsLootTableStats);
foreach (itemEntry entry in itemList)
{
stpWorkspace.Children.Add(entry.bdrItem);
}
}
And here's the ItemEntry class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows;
namespace Random_Item_Giver_Updater
{
public class itemEntry
{
//Controls
public Border bdrItem = new Border();
private Canvas cvsItem = new Canvas();
private TextBlock tblItemName = new TextBlock();
private TextBlock tblItemNBT = new TextBlock();
public Button btnDelete = new Button();
private TextBox tbItemName = new TextBox();
private Button btnSaveItemName = new Button();
private TextBox tbItemNBT = new TextBox();
private Button btnSaveItemNBT = new Button();
public TextBlock tblEntryIndex = new TextBlock();
private TextBlock tblIndicator = new TextBlock();
//Item attributes
public string itemName;
public string itemNBT;
public string newName;
public string newNBT;
private int itemIndex;
public bool isModified = false;
public bool isDeleted = false;
//Reference to main window
MainWindow wndMain = (MainWindow)Application.Current.MainWindow;
//-- Constructor --//
public itemEntry(string name, string nbt, int index)
{
//Initialize variables
itemName = name.TrimEnd('\r', '\n');
itemNBT = nbt.TrimEnd('\r', '\n'); ;
itemIndex = index;
newName = itemName;
newNBT = itemNBT;
//Set backcolor
if (index % 2 == 0)
{
cvsItem.Background = new SolidColorBrush(Color.FromArgb(100, 70, 70, 70));
}
else
{
cvsItem.Background = new SolidColorBrush(Color.FromArgb(100, 90, 90, 90));
}
cvsItem.Height = 50;
//Create itemborder
bdrItem.Margin = new Thickness(0, 0, 0, 0);
bdrItem.Child = cvsItem;
bdrItem.HorizontalAlignment = HorizontalAlignment.Stretch;
bdrItem.VerticalAlignment = VerticalAlignment.Top;
//Create item name text
tblItemName.Margin = new Thickness(10, 10, 0, 0);
tblItemName.Text = itemName;
tblItemName.FontSize = 20;
tblItemName.Foreground = new SolidColorBrush(Colors.White);
cvsItem.Children.Add(tblItemName);
tblItemName.MouseDown += new MouseButtonEventHandler(tblItemName_MouseDown);
//Create delete button
btnDelete.Height = 25;
btnDelete.Width = 100;
btnDelete.Content = "Delete";
btnDelete.Margin = new Thickness(wndMain.ActualWidth - 460, 10, 0, 0);
btnDelete.Click += new RoutedEventHandler(btnDelete_Click);
cvsItem.Children.Add(btnDelete);
//Create item name save button
btnSaveItemName.Height = 25;
btnSaveItemName.Width = 100;
btnSaveItemName.Content = "Confirm";
btnSaveItemName.Margin = new Thickness(370, 10, 0, 0);
btnSaveItemName.Click += new RoutedEventHandler(btnSaveItemName_Click);
cvsItem.Children.Add(btnSaveItemName);
btnSaveItemName.Visibility = Visibility.Hidden;
//Create item name textbox
tbItemName.Width = 350;
tbItemName.Height = 25;
tbItemName.FontSize = 18;
tbItemName.Margin = new Thickness(10, 10, 0, 0);
tbItemName.Visibility = Visibility.Hidden;
cvsItem.Children.Add(tbItemName);
//Create item NBT save button
btnSaveItemNBT.Height = 25;
btnSaveItemNBT.Width = 100;
btnSaveItemNBT.Content = "Confirm";
btnSaveItemNBT.Margin = new Thickness(859, 10, 0, 0);
btnSaveItemNBT.Click += new RoutedEventHandler(btnSaveItemNBT_Click);
cvsItem.Children.Add(btnSaveItemNBT);
btnSaveItemNBT.Visibility = Visibility.Hidden;
//Create item NBT textbox
tbItemNBT.Width = 350;
tbItemNBT.Height = 25;
tbItemNBT.FontSize = 18;
tbItemNBT.Margin = new Thickness(500, 10, 0, 0);
tbItemNBT.Visibility = Visibility.Hidden;
cvsItem.Children.Add(tbItemNBT);
//Create item NBT text
if (itemNBT != "none")
{
tblItemNBT.Margin = new Thickness(500, 10, 0, 0);
tblItemNBT.Text = string.Format("NBT: {0}", itemNBT);
tblItemNBT.FontSize = 20;
tblItemNBT.Foreground = new SolidColorBrush(Colors.White);
cvsItem.Children.Add(tblItemNBT);
tblItemNBT.MouseDown += new MouseButtonEventHandler(tblItemNBT_MouseDown);
}
//Create entry index
tblEntryIndex.Margin = new Thickness(wndMain.ActualWidth - 345, 10, 0, 0);
tblEntryIndex.Text = (itemIndex + 1).ToString();
tblEntryIndex.TextAlignment = TextAlignment.Center;
tblEntryIndex.FontSize = 20;
tblEntryIndex.FontWeight = FontWeights.DemiBold;
tblEntryIndex.Foreground = new SolidColorBrush(Colors.White);
cvsItem.Children.Add(tblEntryIndex);
//Create Indicator
tblIndicator.Margin = new Thickness(wndMain.ActualWidth - 490, 5, 0, 0);
tblIndicator.FontWeight = FontWeights.Bold;
tblIndicator.FontSize = 24;
cvsItem.Children.Add(tblIndicator);
}
//-- Event Handlers --//
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
if (isDeleted == false) //Set state to deleted
{
isModified = true;
isDeleted = true;
btnDelete.Content = "Undo deletion";
SetIndicatorState();
}
else if (isDeleted == true) //If the item has been deleted, set state to undeleted
{
//Check if the item has been modified in some other way before setting the modified state to false
if (itemName == newName)
{
isModified = false;
}
isDeleted = false;
btnDelete.Content = "Delete";
SetIndicatorState();
}
}
private void btnSaveItemName_Click(object sender, RoutedEventArgs e)
{
//Hide the controls for editing and show the item name
tbItemName.Visibility = Visibility.Hidden;
btnSaveItemName.Visibility = Visibility.Hidden;
tblItemName.Visibility = Visibility.Visible;
newName = tbItemName.Text;
tblItemName.Text = newName;
//Check if the item name has been changed and change modified state
if (newName != itemName)
{
isModified = true;
SetIndicatorState();
}
else
{
if (isDeleted == false && itemNBT == newNBT)
{
isModified = false;
}
SetIndicatorState();
}
}
private void btnSaveItemNBT_Click(object sender, RoutedEventArgs e)
{
//Hide the controls for editing and show the item NBT
tbItemNBT.Visibility = Visibility.Hidden;
btnSaveItemNBT.Visibility = Visibility.Hidden;
tblItemNBT.Visibility = Visibility.Visible;
newNBT = tbItemNBT.Text;
tblItemNBT.Text = string.Format("NBT: {0}", newNBT);
//Check if the item NBT has been changed and change modified state
if (newNBT != itemNBT)
{
isModified = true;
SetIndicatorState();
}
else
{
if (isDeleted == false && itemName == newName)
{
isModified = false;
}
SetIndicatorState();
}
}
private void tblItemName_MouseDown(object sender, MouseEventArgs e)
{
//Show the controls for editing and hide the original name
tbItemName.Visibility = Visibility.Visible;
btnSaveItemName.Visibility = Visibility.Visible;
tblItemName.Visibility = Visibility.Hidden;
tbItemName.Text = tblItemName.Text;
}
private void tblItemNBT_MouseDown(object sender, MouseEventArgs e)
{
//Show the controls for editing and hide the original NBT
tbItemNBT.Visibility = Visibility.Visible;
btnSaveItemNBT.Visibility = Visibility.Visible;
tblItemNBT.Visibility = Visibility.Hidden;
tbItemNBT.Text = tblItemNBT.Text.Replace("NBT: ", "");
}
//-- Custom Methods --//
private void SetIndicatorState() {
if(isModified == true && isDeleted == true)
{
//Item deleted, show indicator
tblIndicator.Visibility = Visibility.Visible;
tblIndicator.Text = "X";
tblIndicator.Foreground = new SolidColorBrush(Colors.Red);
}
else if (isModified == true && isDeleted == false)
{
//Item modified, show indicator
tblIndicator.Visibility = Visibility.Visible;
tblIndicator.Text = "#";
tblIndicator.Foreground = new SolidColorBrush(Colors.LightBlue);
}
else if(isModified == false && isDeleted == false)
{
//No changes, hide indicator
tblIndicator.Visibility = Visibility.Hidden;
}
else
{
//Invalid state provided, hide indicator
tblIndicator.Visibility = Visibility.Hidden;
}
}
}
}