Groups | Blog | Home
all groups > c# > january 2006 >

c# : Windows Service Memory Usage



nautonnier
1/21/2006 11:40:17 PM
I know my problem has been discussed ad nauseum but I still don't get it so
please bear with me.

I have written a service which performs some work against a database once a
day (usually in the wee hours of the morning). From the time the service
starts to the first time it hits the database its memory consumption is
about 6.9MB which is a figure I can live with. However, after it hits the
database for the first time its memory jumps to 15MB and stays there. Now,
I'm a realist so 15MB isn't all that bad. I'm just looking to make the
service as slim as possible. so I would like, after it's finished with its
work if the memory could go back down to ~7MB. I'm wondering if there's a
better way to get rid of my database objects than what I'm doing now. Here's
the relevant portion of my service:

public class MyService: System.ServiceProcess.ServiceBase
{
private string timeToWork = "02:00";
private string startupVar2 = "something else";
private string configFileName = "MyService.cfg";
private System.Timers.Timer timer1 = null;
private EventLog appLog = null;
private SqlConnection sqlConnection = null;
private SqlDataAdapter da = null;
private DataTable dt = null;

public ETSReminder()
{
this.ServiceName = "MyService";
this.CanStop = true;
this.AutoLog = false;

appLog = new EventLog();
appLog.Source = "MyService";
}

// OnStart method starts the timer and adds the Elapsed event
// OnStop closes or disposes the service objects (

private void timer1_Elapsed(object sender,
System.Timers.ElapsedEventArgs e)
{
if (DateTime.Now.TimeOfDay >
Convert.ToDateTime(timeToWork).TimeOfDay &&
DateTime.Now.TimeOfDay <
Convert.ToDateTime(timeToWork).AddMinutes(5.5).TimeOfDay)
{
DoWork();
}
}

private void DoWork()
{
try
{
ArrayList connectionStrings =
GetConnectionStrings(configFileName);
sqlConnection = new SqlConnection();

SqlCommand cmd1 = new SqlCommand("sproc1");
cmdCleanup.CommandType = CommandType.StoredProcedure;
cmdCleanup.Connection = sqlConnection;

SqlCommand cmd2 = new SqlCommand("sproc2");
cmdUserList.CommandType = CommandType.StoredProcedure;
cmdUserList.Connection = sqlConnection;

SqlCommand cmd3 = new SqlCommand("sproc3");
cmdReminderSent.CommandType = CommandType.StoredProcedure;
cmdReminderSent.Connection = sqlConnection;

foreach (string connectionString in connectionStrings)
{
sqlConnection.ConnectionString = connectionString;
sqlConnection.Open();

cmd1.ExecuteNonQuery();

da = new SqlDataAdapter(cmd2);
dt = new DataTable();
da.Fill(dt);

foreach (DataRow dr in dt.Rows)
{
// Does stuff including sending some email
cmd3.ExecuteNonQuery();
}

sqlConnection.Close();
}
}
catch
{
// Handle any exceptions
}
finally
{
dt.Dispose();
da.Dispose();
sqlConnection.Dispose();
}
}

}

---end code---

I have the database objects as class variables in case something hangs while
it's working, I can close the connection from my error handling routines.

I have even tried adding GC.Collect() after I dispose sqlConnection but that
doesn't seem to make the memory ever go down.

I have another more sinister problem in that each time the timer1_Elapsed
runs about 20K of memory is added that never goes away. Since this is a 24/7
service this could be an even bigger problem.

I have no ego over my code so if I'm doing something dumb please tell me.
Otherwise I would love to hear any ideas how I may solve my two memory
issues.

Thanks,
nautonnier

nautonnier
1/21/2006 11:44:59 PM
you know where it says "public ETSReminder()"? you should probably change
that in your head to "public MyService()" and forget you ever saw what it
was really called.<g>

Peter Bromberg [C# MVP]
1/22/2006 6:46:02 AM
Nautonnier,
if your service only needs to perform its action once per day, I believe you
would be far better off with a console Executable that gets kicked off at the
proper time via an entry in Task Scheduler.

In this manner, once it's done, it will exit cleanly and there will be no
memory consumption for the next 24 hours.
Peter

--
Co-founder, Eggheadcafe.com developer portal:
http://www.eggheadcafe.com
UnBlog:
http://petesbloggerama.blogspot.com




[quoted text, click to view]
nautonnier
1/22/2006 4:03:06 PM
That's not a bad idea Peter, except that I have no control over what happens
in the client environment and Task Scheduler may not even be enabled. Thus,
it was decided to go with a service.

-nautonnier


[quoted text, click to view]

David Hernandez Diez
1/22/2006 5:36:10 PM
Have you tried running a memory profiler.

there are many commercial profilers like Devpartner (compuware) or ANTS
(redgate) ...

Or you can use a free one from Microsoft (CLR profiler) .. It may give
you a clue of what is happening in your code.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp


--
Regards,
David Hernández Díez
MCDBA MCSD vs6 & .NET
DCE5 .Net1.1 & DCE2 .NET 2.0

[quoted text, click to view]
Willy Denoyette [MVP]
1/23/2006 9:57:14 AM
Task scheduler itself is a service, if a user is allowed to disable the
scheduler, who will stop him to disable your service?
It's a bad idea to continuously waste precious resources for a service that
effectively does some work only once per 24 hours for about a few seconds.

Willy.


[quoted text, click to view]
| That's not a bad idea Peter, except that I have no control over what
happens
| in the client environment and Task Scheduler may not even be enabled.
Thus,
| it was decided to go with a service.
|
| -nautonnier
|
|
[quoted text, click to view]
| > Nautonnier,
| > if your service only needs to perform its action once per day, I believe
| you
| > would be far better off with a console Executable that gets kicked off
at
| the
| > proper time via an entry in Task Scheduler.
| >
| > In this manner, once it's done, it will exit cleanly and there will be
no
| > memory consumption for the next 24 hours.
| > Peter
| >
| > --
| > Co-founder, Eggheadcafe.com developer portal:
| > http://www.eggheadcafe.com
| > UnBlog:
| > http://petesbloggerama.blogspot.com
| >
| >
| >
| >
[quoted text, click to view]
| >
| > > I know my problem has been discussed ad nauseum but I still don't get
it
| so
| > > please bear with me.
| > >
| > > I have written a service which performs some work against a database
| once a
| > > day (usually in the wee hours of the morning). From the time the
service
| > > starts to the first time it hits the database its memory consumption
is
| > > about 6.9MB which is a figure I can live with. However, after it hits
| the
| > > database for the first time its memory jumps to 15MB and stays there.
| Now,
| > > I'm a realist so 15MB isn't all that bad. I'm just looking to make the
| > > service as slim as possible. so I would like, after it's finished with
| its
| > > work if the memory could go back down to ~7MB. I'm wondering if
there's
| a
| > > better way to get rid of my database objects than what I'm doing now.
| Here's
| > > the relevant portion of my service:
| > >
| > > public class MyService: System.ServiceProcess.ServiceBase
| > > {
| > > private string timeToWork = "02:00";
| > > private string startupVar2 = "something else";
| > > private string configFileName = "MyService.cfg";
| > > private System.Timers.Timer timer1 = null;
| > > private EventLog appLog = null;
| > > private SqlConnection sqlConnection = null;
| > > private SqlDataAdapter da = null;
| > > private DataTable dt = null;
| > >
| > > public ETSReminder()
| > > {
| > > this.ServiceName = "MyService";
| > > this.CanStop = true;
| > > this.AutoLog = false;
| > >
| > > appLog = new EventLog();
| > > appLog.Source = "MyService";
| > > }
| > >
| > > // OnStart method starts the timer and adds the Elapsed event
| > > // OnStop closes or disposes the service objects (
| > >
| > > private void timer1_Elapsed(object sender,
| > > System.Timers.ElapsedEventArgs e)
| > > {
| > > if (DateTime.Now.TimeOfDay >
| > > Convert.ToDateTime(timeToWork).TimeOfDay &&
| > > DateTime.Now.TimeOfDay <
| > > Convert.ToDateTime(timeToWork).AddMinutes(5.5).TimeOfDay)
| > > {
| > > DoWork();
| > > }
| > > }
| > >
| > > private void DoWork()
| > > {
| > > try
| > > {
| > > ArrayList connectionStrings =
| > > GetConnectionStrings(configFileName);
| > > sqlConnection = new SqlConnection();
| > >
| > > SqlCommand cmd1 = new SqlCommand("sproc1");
| > > cmdCleanup.CommandType = CommandType.StoredProcedure;
| > > cmdCleanup.Connection = sqlConnection;
| > >
| > > SqlCommand cmd2 = new SqlCommand("sproc2");
| > > cmdUserList.CommandType = CommandType.StoredProcedure;
| > > cmdUserList.Connection = sqlConnection;
| > >
| > > SqlCommand cmd3 = new SqlCommand("sproc3");
| > > cmdReminderSent.CommandType = CommandType.StoredProcedure;
| > > cmdReminderSent.Connection = sqlConnection;
| > >
| > > foreach (string connectionString in connectionStrings)
| > > {
| > > sqlConnection.ConnectionString = connectionString;
| > > sqlConnection.Open();
| > >
| > > cmd1.ExecuteNonQuery();
| > >
| > > da = new SqlDataAdapter(cmd2);
| > > dt = new DataTable();
| > > da.Fill(dt);
| > >
| > > foreach (DataRow dr in dt.Rows)
| > > {
| > > // Does stuff including sending some email
| > > cmd3.ExecuteNonQuery();
| > > }
| > >
| > > sqlConnection.Close();
| > > }
| > > }
| > > catch
| > > {
| > > // Handle any exceptions
| > > }
| > > finally
| > > {
| > > dt.Dispose();
| > > da.Dispose();
| > > sqlConnection.Dispose();
| > > }
| > > }
| > >
| > > }
| > >
| > > ---end code---
| > >
| > > I have the database objects as class variables in case something hangs
| while
| > > it's working, I can close the connection from my error handling
| routines.
| > >
| > > I have even tried adding GC.Collect() after I dispose sqlConnection
but
| that
| > > doesn't seem to make the memory ever go down.
| > >
| > > I have another more sinister problem in that each time the
| timer1_Elapsed
| > > runs about 20K of memory is added that never goes away. Since this is
a
| 24/7
| > > service this could be an even bigger problem.
| > >
| > > I have no ego over my code so if I'm doing something dumb please tell
| me.
| > > Otherwise I would love to hear any ideas how I may solve my two memory
| > > issues.
| > >
| > > Thanks,
| > > nautonnier
| > >
| > >
| > >
|
|

nautonnier
1/23/2006 11:57:13 AM
My point was all this was discussed with the client and this was the way
they wanted to go.

[quoted text, click to view]

nautonnier
1/23/2006 2:43:45 PM
So I guess my first pointed question is wouldn't the call to dispose of the
connection, dataadapter and datatable objects mark them so the GC reclaims
their memory and shouldn't that conceivably occur within 5 minutes? Would
this not also be true of the command objects once they went out of scope at
the end of DoWork()?

At this point I'm trying to understand things at a deeper level and
hopefully become a better programmer.

[quoted text, click to view]

AddThis Social Bookmark Button