Question

Alright so basicly I have this simple application running in system tray that has one timer. Every tick it performs a check to see if a given directory and file exists, and based on the result it changes its icon.

The problem is every single timer tick the memory for the application raises ~100kb. I currently have it running for about 5 mins and it already uses 40MB of memory, which is unacceptable for such "micro" application.

Here's my code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace Tray
{
    public partial class Main : Form
    {
        string drive = "C:\\";
        string file  = "test.txt";

        System.Drawing.Image imgRed     = Image.FromFile("res\\icon-red.png");
        System.Drawing.Image imgOrange  = Image.FromFile("res\\icon-orange.png");
        System.Drawing.Image imgGreen   = Image.FromFile("res\\icon-green.png");
        System.Drawing.Icon  icoRed      = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-red.ico");
        System.Drawing.Icon  icoOrange   = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-orange.ico");
        System.Drawing.Icon  icoGreen    = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-green.ico");

        public Main()
        {
            InitializeComponent();
        }

        public static string ShowPrompt(string text, string caption)
        {
            Form prompt = new Form();
            prompt.Width = 500;
            prompt.Height = 150;
            prompt.Text = caption;
            Label textLabel = new Label() { Left = 50, Top = 20, Text = text };
            TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 };
            Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70 };
            confirmation.Click += (sender, e) => { prompt.Close(); };
            prompt.Controls.Add(confirmation);
            prompt.Controls.Add(textLabel);
            prompt.Controls.Add(textBox);
            prompt.ShowDialog();
            return textBox.Text;
        }

        public void updateInfo(){
            this.statusDrive.Text = "Drive [" + drive + "]";
            this.statusFile.Text = "File [" + drive + file + "]";
        }

        public void exec(){
            int status = 0;

            this.trayIcon.Text = "[Drive - ";
            if (Directory.Exists(drive)){
                this.statusDrive.Text += " - OK";
                this.statusDrive.Image = imgGreen;
                status++;
                this.trayIcon.Text += "OK] ";
            } else{
                this.statusDrive.Text += " - FAIL";
                this.statusDrive.Image = imgRed;
                this.trayIcon.Text += "FAIL] ";
            }

            this.trayIcon.Text += "[File - ";
            if (File.Exists(drive + file))
            {
                this.statusFile.Text += " - OK";
                this.statusFile.Image = imgGreen;
                status++;
                this.trayIcon.Text += "OK] ";
            }
            else
            {
                this.statusFile.Text += " - FAIL";
                this.statusFile.Image = imgRed;
                this.trayIcon.Text += "FAIL] ";
            }

            switch (status)
            {
                case 2:
                    this.Icon = icoGreen;
                    this.trayIcon.Icon = icoGreen;
                    this.status.Image = imgGreen;
                    break;
                case 1:
                    this.Icon = icoOrange;
                    this.trayIcon.Icon = icoOrange;
                    this.status.Image = imgOrange;
                    break;
                case 0:
                default:
                    this.Icon = icoRed;
                    this.trayIcon.Icon = icoRed;
                    this.status.Image = imgRed;
                    break;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Hide();

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            updateInfo();
            exec();
        }

        private void chDrive_Click(object sender, EventArgs e)
        {
            this.drive = ShowPrompt("Enter drive path", "Change drive");
        }

        private void chFile_Click(object sender, EventArgs e)
        {
            this.file = ShowPrompt("Enter new file path:", "Change file");
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}

I already tried to optimize the app by preloading the icons and images into variables and assigning those to the appropriate properties, however this didn't solve my problem.

Also, note that I managed to hide my main window by doing this (in Program.cs):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Tray
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Main mForm = new Main();
            Application.Run();
        }
    }
}

UPDATE

I just noticed that the memory usage climbs up to 50MB and drops to 20MB afterwards (and goes up again). Is this something I can possibly address or is it a windows "issue"?

Était-ce utile?

La solution

I'm going to take a stab at it being the string concatenations happening once a second. Consider using a StringBuilder. 40MB is nothing though really.

RE: Your update. The Garbage Collector is reclaiming the memory as it sees fit.

Autres conseils

You never appear to be disposing your form correctly in ShowPrompt, so I'd imagine this is your problem.

Because a form displayed as a dialog box is not closed, you must call the Dispose method of the form when the form is no longer needed by your application.

ShowDialog

Some points that could cut down on memory usage:

Try to prebuild all those strings you're building in exec(). It looks like they're all runtime constants, but you build them every tick instead of building them once when the application starts. If this isn't possible, use StringBuilder instead of +=.

Only change properties on controls (icon, trayText, etc) if there has been a change. I.E. if tray text is already "[Drive C:\ - OK]", don't set its value again to "[Drive C:\ - OK]" next tick.

Garbage Collector does all the work of memory management for you. Temporary rise in memory doesn't always mean that there is a memory leak. It may come down when the GC collects memory. In case you suspect that there are memory leaks you need to do memory profiling which is no easy job. You need to read into this article for steps that you can take to find out the problem in your code. Alternatively, there are multiple tools avaiable in the market to do this job for you. You can use Ants Profiler of Red Gate, memprofiler amongst others.

One thing you might consider is rather than using a timer why not use the FileSystemWatcher and attach to events:

var watcher = new FileSystemWatcher("somepath");
watcher.Deleted += (sender, eventArgs) => { };
watcher.Changed += (sender, eventArgs) => { };
watcher.Error += (sender, eventArgs) => { };
watcher.Renamed += (sender, eventArgs) => { };

I also agree that you should be disposing of the forms once you're done with them.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top