reference not removed revisited

F

Frank

Hello,
I again ask for help on the next problem, a previous question did not give a
solution.

The method at the bottom of this message leaves a reference to somethig, as
far as I can tell to the _sqladapter variable. As you can see I use the
'using' statement so the adapter and the connection should be neatly
disposed of.
But it doesn't!
I will be praying for a solution.
Thanks
Frank


public DataTable lees()
{
string ConnectString="Server= localhost; database=aa; User
id=uniserver;password=aa;Connect Timeout=100;";
DataSet _DataSet= new DataSet();
string StoredProcedure="S_aa";
using (SqlConnection _SqlConnection= new SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{
_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
 
I

Ignacio Machin \( .NET/ C# MVP \)

HI,

How do you know it?

Also remember that the GC is not deterministic, all you can assure is that
the Dispose method will be called ( cause of the using )

Now, do this. inherit from SqlDataAdapter and override Dispose , then you
will see when it's called.
IIRC Dispose call Close , so if it's called as expected the connection is
closed and the objects are ready to be GC.

Cheers,
 
F

Frank

Ignacio,

I use http://www.scitech.se/memprofiler/Default.htm to see the refs. It's a
weakreference.
I'll try your suggestion with the inherit.
Frank
Ignacio Machin ( .NET/ C# MVP ) said:
HI,

How do you know it?

Also remember that the GC is not deterministic, all you can assure is that
the Dispose method will be called ( cause of the using )

Now, do this. inherit from SqlDataAdapter and override Dispose , then you
will see when it's called.
IIRC Dispose call Close , so if it's called as expected the connection is
closed and the objects are ready to be GC.

Cheers,

--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation

Frank said:
Hello,
I again ask for help on the next problem, a previous question did not
give a solution.

The method at the bottom of this message leaves a reference to somethig,
as far as I can tell to the _sqladapter variable. As you can see I use
the 'using' statement so the adapter and the connection should be neatly
disposed of.
But it doesn't!
I will be praying for a solution.
Thanks
Frank


public DataTable lees()
{
string ConnectString="Server= localhost; database=aa; User
id=uniserver;password=aa;Connect Timeout=100;";
DataSet _DataSet= new DataSet();
string StoredProcedure="S_aa";
using (SqlConnection _SqlConnection= new SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
 
F

Frank

Ignacio,
I tried the inherit but I get an error at 'public class
newclass:SqlDataAdapter'.
Do I have to do that in another way?
Regards
Frank
Ignacio Machin ( .NET/ C# MVP ) said:
HI,

How do you know it?

Also remember that the GC is not deterministic, all you can assure is that
the Dispose method will be called ( cause of the using )

Now, do this. inherit from SqlDataAdapter and override Dispose , then you
will see when it's called.
IIRC Dispose call Close , so if it's called as expected the connection is
closed and the objects are ready to be GC.

Cheers,

--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation

Frank said:
Hello,
I again ask for help on the next problem, a previous question did not
give a solution.

The method at the bottom of this message leaves a reference to somethig,
as far as I can tell to the _sqladapter variable. As you can see I use
the 'using' statement so the adapter and the connection should be neatly
disposed of.
But it doesn't!
I will be praying for a solution.
Thanks
Frank


public DataTable lees()
{
string ConnectString="Server= localhost; database=aa; User
id=uniserver;password=aa;Connect Timeout=100;";
DataSet _DataSet= new DataSet();
string StoredProcedure="S_aa";
using (SqlConnection _SqlConnection= new SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi Frank,


Unfortunally SqlDatAdapter is marked as sealed :(

So what I proposed before cannot be done, sorry for the mistake, I did not
check that SqlDataAdapter was sealed. ( ITOH I do not see why it's sealed in
the first place)


As Voss noted a weakreference can be GC , so what I propose you to do is
give time to the GC to run ( or force it using GC.Collect () ) and see what
happens.


cheers,

--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation



Frank said:
Ignacio,
I tried the inherit but I get an error at 'public class
newclass:SqlDataAdapter'.
Do I have to do that in another way?
Regards
Frank
Ignacio Machin ( .NET/ C# MVP ) said:
HI,

How do you know it?

Also remember that the GC is not deterministic, all you can assure is
that the Dispose method will be called ( cause of the using )

Now, do this. inherit from SqlDataAdapter and override Dispose , then you
will see when it's called.
IIRC Dispose call Close , so if it's called as expected the connection is
closed and the objects are ready to be GC.

Cheers,

--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation

Frank said:
Hello,
I again ask for help on the next problem, a previous question did not
give a solution.

The method at the bottom of this message leaves a reference to somethig,
as far as I can tell to the _sqladapter variable. As you can see I use
the 'using' statement so the adapter and the connection should be neatly
disposed of.
But it doesn't!
I will be praying for a solution.
Thanks
Frank


public DataTable lees()
{
string ConnectString="Server= localhost; database=aa; User
id=uniserver;password=aa;Connect Timeout=100;";
DataSet _DataSet= new DataSet();
string StoredProcedure="S_aa";
using (SqlConnection _SqlConnection= new SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
 
W

Willy Denoyette [MVP]

Frank said:
Hello,
I again ask for help on the next problem, a previous question did not give
a solution.

The method at the bottom of this message leaves a reference to somethig,
as far as I can tell to the _sqladapter variable. As you can see I use the
'using' statement so the adapter and the connection should be neatly
disposed of.
But it doesn't!
I will be praying for a solution.
Thanks
Frank


public DataTable lees()
{
string ConnectString="Server= localhost; database=aa; User
id=uniserver;password=aa;Connect Timeout=100;";
DataSet _DataSet= new DataSet();
string StoredProcedure="S_aa";
using (SqlConnection _SqlConnection= new SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}

Both are neatly disposed, that is Dispose is called on both objects at the
end of the using block.
Seems like you are confusing "Disposing" with "object lifetime", when
Dispose is called on a Dataset the underlying memory resources will be
released, when called on a SqlConnection the connection returns to the pool.
The object references (_DataSet, _SqlConnection and _SqlDataAdapter ) are
nulled on return and the objects they refer to become eligible for
collection by the GC, however, this clean-up is non deterministic so it
might take a while before it happens and is depending on your object
allocation scheme.

If you aren't sure about this you can register a Disposed event handler ;-)

using (SqlConnection _SqlConnection = new
SqlConnection(ConnectString))
{
_SqlConnection.Disposed += new EventHandler( OnSQLDisposed);


static void OnSQLDisposed(object sender, EventArgs e)
{
Console.WriteLine("Disposed {0}", sender);
}

Willy.
 
F

Frank

Hi,
I tried a forced GC: no difference. I tried this program in a loop and after
500.000 instances I have have 500.000 references. It's just not
garbage collected.
Greets
Frank
Ignacio Machin ( .NET/ C# MVP ) said:
Hi Frank,


Unfortunally SqlDatAdapter is marked as sealed :(

So what I proposed before cannot be done, sorry for the mistake, I did not
check that SqlDataAdapter was sealed. ( ITOH I do not see why it's sealed
in the first place)


As Voss noted a weakreference can be GC , so what I propose you to do is
give time to the GC to run ( or force it using GC.Collect () ) and see
what happens.


cheers,

--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation



Frank said:
Ignacio,
I tried the inherit but I get an error at 'public class
newclass:SqlDataAdapter'.
Do I have to do that in another way?
Regards
Frank
Ignacio Machin ( .NET/ C# MVP ) said:
HI,

How do you know it?

Also remember that the GC is not deterministic, all you can assure is
that the Dispose method will be called ( cause of the using )

Now, do this. inherit from SqlDataAdapter and override Dispose , then
you will see when it's called.
IIRC Dispose call Close , so if it's called as expected the connection
is closed and the objects are ready to be GC.

Cheers,

--
Ignacio Machin,
ignacio.machin AT dot.state.fl.us
Florida Department Of Transportation

Hello,
I again ask for help on the next problem, a previous question did not
give a solution.

The method at the bottom of this message leaves a reference to
somethig, as far as I can tell to the _sqladapter variable. As you can
see I use the 'using' statement so the adapter and the connection
should be neatly disposed of.
But it doesn't!
I will be praying for a solution.
Thanks
Frank


public DataTable lees()
{
string ConnectString="Server= localhost; database=aa; User
id=uniserver;password=aa;Connect Timeout=100;";
DataSet _DataSet= new DataSet();
string StoredProcedure="S_aa";
using (SqlConnection _SqlConnection= new
SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
 
W

Willy Denoyette [MVP]

Frank said:
Hi,
I tried a forced GC: no difference. I tried this program in a loop and
after 500.000 instances I have have 500.000 references. It's just not
garbage collected.
Greets
Frank

Reference to what object and what kind of references (hard/weak)? If you
have 500000 life references your workingset would be high (size depends on
the object). Note also that weakreferences don't stop the GC to collect the
objects refered by.

It also doesn't make sense to call GC.Collect, the GC knows better than you
do when it should run.

Did you try to add this to your code...


using (SqlConnection _SqlConnection = new
SqlConnection(ConnectString))
{
_SqlConnection.Disposed += new EventHandler( OnSQLDisposed);
..

static void OnSQLDisposed(object sender, EventArgs e)
{
Console.WriteLine("Disposed {0}", sender);
}

You will see that Dispose is called.

Another thing I would like to see is your complete code.

Willy.
 
W

Willy Denoyette [MVP]

Frank said:
Willy,
they are weak references, as far as I can tell to the sqladapter.
This is the whole code, I call this method from the main in a
consoleapplication in a loop.
I can see the references with a tool:
http://www.scitech.se/memprofiler/Default.htm
Regards
Frank

I didn't use this tool when I ran your code executing a simple stored
procedure, instead I attached the process to windbg (native debugger) and
used sos.dll to watch the CLR internals.

Here is what I have noticed/measure (your mileage may vary !!) ...

The code iterates 1000 times per second, that means:
-1000 SqlConnection, 1000 DataSet, 1000 SqlDataAdapter and 1000 DataTable
objects created per second, and 1000 SqlConnection and SqlDataAdapter
Disposed.
- each loop returns a DataTable, so 1000 DataTables are returned per sec.
- The GC runs ~40 times per second to collect Gen0, ~1 time/second to
collect Gen0 & Gen1, one or two times during the complete run (500 secs) to
collect Gen2 (full collect).
The number of surviving WeakReferences that reach into Gen2 is about 50 per
second. (16 bytes/WeakReference).
When the GC does a full collect all WeakReferences are removed from the
heap.

Conclusion:
The maximum number of WeakReferences per run was not higher than ~25000
(400000 bytes), and they are removed as expected when the GC does a full
collect.
Seems like the memprofiler is so intruding that he disturbs normal GC run.
So I would say - nothing to worry about.

Willy.
 
F

Frank

This is helpfull Willy, thank you very much.
I'll contact scitech about this.
Greets
Frank
 
F

Frank

I'm not confusing them. In the program I use to see references to objects it
says 'live instances' to sqlAdapter.



Willy Denoyette said:
Frank said:
Hello,
I again ask for help on the next problem, a previous question did not
give a solution.

The method at the bottom of this message leaves a reference to somethig,
as far as I can tell to the _sqladapter variable. As you can see I use
the 'using' statement so the adapter and the connection should be neatly
disposed of.
But it doesn't!
I will be praying for a solution.
Thanks
Frank


public DataTable lees()
{
string ConnectString="Server= localhost; database=aa; User
id=uniserver;password=aa;Connect Timeout=100;";
DataSet _DataSet= new DataSet();
string StoredProcedure="S_aa";
using (SqlConnection _SqlConnection= new SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}

Both are neatly disposed, that is Dispose is called on both objects at the
end of the using block.
Seems like you are confusing "Disposing" with "object lifetime", when
Dispose is called on a Dataset the underlying memory resources will be
released, when called on a SqlConnection the connection returns to the
pool.
The object references (_DataSet, _SqlConnection and _SqlDataAdapter ) are
nulled on return and the objects they refer to become eligible for
collection by the GC, however, this clean-up is non deterministic so it
might take a while before it happens and is depending on your object
allocation scheme.

If you aren't sure about this you can register a Disposed event handler
;-)

using (SqlConnection _SqlConnection = new
SqlConnection(ConnectString))
{
_SqlConnection.Disposed += new EventHandler( OnSQLDisposed);


static void OnSQLDisposed(object sender, EventArgs e)
{
Console.WriteLine("Disposed {0}", sender);
}

Willy.
 
W

Willy Denoyette [MVP]

Frank said:
I'm not confusing them. In the program I use to see references to objects
it says 'live instances' to sqlAdapter.

Sorry, but the point is that
1. _SqlDataAdapter.Dipose is called at the end of the using block.
2. _SqlDataAdapter is nulled at the end of the same block, so the object
instance is eligible to be collected by the GC.

That means it will be collected at some point in time, but that point is
non-determnistic, it depends on several factors like the Generation the
object belongs to at the moment the GC runs.
If the object is actually in Gen2, it will only be collected when the GC
does a full collect.

Willy.
 
F

Frank

I disagree, sqladapter will not be collected because it has live references
to it it says so by my debugprogram.
Frank
 
W

Willy Denoyette [MVP]

Frank said:
I disagree, sqladapter will not be collected because it has live references
to it it says so by my debugprogram.
Frank

The sample you posted has one hard reference which is scoped by the using
block, that means that the reference is nulled when leaving the scope.
On another thread you said they were WeakReferences, I ran the code in a
loop as you did and noticed the WeakReference growth, but I also said they
were collected with a full collect.

It's your right to disagree, but to be sure we are talking about the same
thing here, may I ask you to post a complete sample that illustrates the
issue, also what do you mean with your debugprogram.

Willy.
 
W

Willy Denoyette [MVP]

Frank said:
Willy,
as I posted earlier I use http://www.scitech.se/memprofiler/Default.htm,
it's free (for 15 days) and very easy to use.
It shows the weakreference as 'live instances', meaning they are used and
cannot be collected.
Regards
Frank


Ran following code (using .NET Framework version 1.1.4322 on XP SP2) through
scitech memory profiler v2.5:
The Database (SQL Server 2000) is located on a separate W2K3 server, over a
100Mb/s switched LAN.

using System;
using System.Data;
using System.Data.SqlClient;
namespace Willys
{
class Tester
{
static void Main()
{
for (int i = 0; i < 500000; i++)
{
DataTable dt = Lees();
if (i % 1000 == 0)
{
Console.WriteLine(i); // show loops done
}
}
Console.ReadLine();
}
static DataTable Lees()
{
string ConnectString="Initial Catalog=Northwind;Data
Source=xxx\\sqlinstance;Integrated Security=SSPI;";
DataSet _DataSet= new DataSet();
string StoredProcedure="Ten Most Expensive Products";
using (SqlConnection _SqlConnection = new
SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{
_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
}
}

This is what I noticed:
the number of Live WeakReferences avarages at 22, very rarely drops to
values like 5 and peaks at 64 (in real-time view). The number of SqlAdapters
varies between 0 and 1 (obviously).
The number of WeakReferences at the end of the run is 3 (two of them are
SqlClient related).

As I said before this is a perfectly normal allocation behavior, if you
observe different results running the same code, you must have a problem
with your set-up.

Willy.
 
F

Frank

Willy,
I am at a loss, the references are indeed removed as expected. But I assure
you they were not when I started this thread. However, I cannot check my
original program as I have it at work, so I will check it tomorrowmorning
(in the Netherlands we have a day off today).
In the meantine, thanks for your time and feedback.

By the way, what do you think of the scitech tool? I think I will register
for a license at work.

Best regards
Frank
Willy Denoyette said:
Frank said:
Willy,
as I posted earlier I use http://www.scitech.se/memprofiler/Default.htm,
it's free (for 15 days) and very easy to use.
It shows the weakreference as 'live instances', meaning they are used and
cannot be collected.
Regards
Frank


Ran following code (using .NET Framework version 1.1.4322 on XP SP2)
through scitech memory profiler v2.5:
The Database (SQL Server 2000) is located on a separate W2K3 server, over
a 100Mb/s switched LAN.

using System;
using System.Data;
using System.Data.SqlClient;
namespace Willys
{
class Tester
{
static void Main()
{
for (int i = 0; i < 500000; i++)
{
DataTable dt = Lees();
if (i % 1000 == 0)
{
Console.WriteLine(i); // show loops done
}
}
Console.ReadLine();
}
static DataTable Lees()
{
string ConnectString="Initial Catalog=Northwind;Data
Source=xxx\\sqlinstance;Integrated Security=SSPI;";
DataSet _DataSet= new DataSet();
string StoredProcedure="Ten Most Expensive Products";
using (SqlConnection _SqlConnection = new
SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
}
}

This is what I noticed:
the number of Live WeakReferences avarages at 22, very rarely drops to
values like 5 and peaks at 64 (in real-time view). The number of
SqlAdapters varies between 0 and 1 (obviously).
The number of WeakReferences at the end of the run is 3 (two of them are
SqlClient related).

As I said before this is a perfectly normal allocation behavior, if you
observe different results running the same code, you must have a problem
with your set-up.

Willy.
 
F

Frank

Willy,
at last!!!!!!!!!!!! I found it. I use [STAThread] in my main class. Removing
that leaves no live instances to weakreferences and adding it gives lots of
references.
Again, thanks for your help.

But, why does this line have this reaction?

Regards

Frank

Willy Denoyette said:
Frank said:
Willy,
as I posted earlier I use http://www.scitech.se/memprofiler/Default.htm,
it's free (for 15 days) and very easy to use.
It shows the weakreference as 'live instances', meaning they are used and
cannot be collected.
Regards
Frank


Ran following code (using .NET Framework version 1.1.4322 on XP SP2)
through scitech memory profiler v2.5:
The Database (SQL Server 2000) is located on a separate W2K3 server, over
a 100Mb/s switched LAN.

using System;
using System.Data;
using System.Data.SqlClient;
namespace Willys
{
class Tester
{
static void Main()
{
for (int i = 0; i < 500000; i++)
{
DataTable dt = Lees();
if (i % 1000 == 0)
{
Console.WriteLine(i); // show loops done
}
}
Console.ReadLine();
}
static DataTable Lees()
{
string ConnectString="Initial Catalog=Northwind;Data
Source=xxx\\sqlinstance;Integrated Security=SSPI;";
DataSet _DataSet= new DataSet();
string StoredProcedure="Ten Most Expensive Products";
using (SqlConnection _SqlConnection = new
SqlConnection(ConnectString))
{
using (SqlDataAdapter _SqlDataAdapter = new
SqlDataAdapter(StoredProcedure,_SqlConnection))
{
try
{

_SqlDataAdapter.SelectCommand.CommandType=CommandType.StoredProcedure;
_SqlDataAdapter.Fill(_DataSet,StoredProcedure);
_SqlDataAdapter.SelectCommand=null;
}
catch (SqlException e)
{
Console.WriteLine(StoredProcedure+e.Message+e.StackTrace);
}
}
}
return _DataSet.Tables[0];
}
}
}

This is what I noticed:
the number of Live WeakReferences avarages at 22, very rarely drops to
values like 5 and peaks at 64 (in real-time view). The number of
SqlAdapters varies between 0 and 1 (obviously).
The number of WeakReferences at the end of the run is 3 (two of them are
SqlClient related).

As I said before this is a perfectly normal allocation behavior, if you
observe different results running the same code, you must have a problem
with your set-up.

Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top