How to Force Disposal of Objects / GC - c#

How do you force objects to dispose after use in order to free up memory? And, how do you force GC to collect?
Here's my Save Code. I've noticed that, every time I execute this function, my memory consumption goes up that would eventually caused an out of memory error after a couple of hits.
protected void btnSaveEmptyOC_Click(object sender, EventArgs e)
{
try
{
if (ViewState["ServiceDetailID"].ToString() != null)
{
CashExpense tblCashExpenses = new CashExpense();
Guid CashExpensesID = Guid.NewGuid();
tblCashExpenses.CashExpensesID = CashExpensesID;
tblCashExpenses.ServiceDetailsID = new Guid(ViewState["ServiceDetailID"].ToString());
tblCashExpenses.Description = txtDescriptionEmptyOC.Text;
tblCashExpenses.Quantity = Decimal.Parse(txtQTYEmptyOC.Text);
tblCashExpenses.UnitCost = Decimal.Parse(txtUnitCostEmptyOC.Text);
tblCashExpenses.CreatedBy = User.Identity.Name;
tblCashExpenses.DateCreated = DateTime.Now;
tblCashExpenses.CashExpensesTypeID = "OTHER";
CashExpenses_worker.insert(tblCashExpenses);
CashExpenses_worker.submit();
//Clear items after saving
txtDescriptionEmptyOC.Text = "";
txtQTYEmptyOC.Text = "";
txtUnitCostEmptyOC.Text = "";
ValidationMessage.ShowValidationMessage(MessageCenter.CashExpenseMaintenace.InsertOC2, "SaveEmptyOC", this.Page);
MyAuditProvider.Insert(this.GetType().ToString(), ViewState["MarginAnalysisID"].ToString(), MessageCenter.Mode.ADD, MessageCenter.CashExpenseMaintenace.InsertOC2, Page.Request, User);
divOtherCost.Visible = false;
grd_othercost.Visible = true;
btnaddothercost.Visible = true;
tblCashExpenses = null;
}
else
{
ValidationMessage.ShowValidationMessage(MessageCenter.CashExpenseMaintenace.SaveServiceDetailOC, "SaveEmptyOC", this.Page);
}
}
catch
{
ValidationMessage.ShowValidationMessage(MessageCenter.CashExpenseMaintenace.InsertOCError, "SaveEmptyOC", this.Page);
}
finally
{
//Rebinds the Grid
populategrd_othercost();
Dispose();
GC.SuppressFinalize(this);
}
}
Here's My business layer class
public class CashExpensesBL
{
CEADataStoreDataContext CashExpensesDB = new CEADataStoreDataContext();
public IEnumerable<CashExpense> get()
{
return CashExpensesDB.CashExpenses;
}
public IEnumerable<CashExpense> get(Expression<Func<CashExpense, Boolean>> express)
{
return CashExpensesDB.CashExpenses.Where(express);
}
public void insert(CashExpense item)
{
CashExpensesDB.CashExpenses.InsertOnSubmit(item);
}
public void delete(CashExpense item)
{
CashExpensesDB.CashExpenses.DeleteOnSubmit(item);
}
public void deleteDC(Guid servicedetailid)
{
CashExpensesDB.sp_deleteDefaultCost(servicedetailid);
}
public void submit()
{
CashExpensesDB.SubmitChanges();
}
}

You should dispose your DataContext. I can't see it being removed anywhere, so the connection will remain open and references may be held on to (preventing the GC from picking them up). This may be what's causing the problem. If you don't want to dispose manually, you can perform the transaction within a using block.
Edit in response to Business Layer update -
You can wrap the methods in using blocks like this:
public void insert(CashExpense item)
{
using(CEADataStoreDataContext CashExpensesDB = new CEADataStoreDataContext())
{
CashExpensesDB.CashExpenses.InsertOnSubmit(item);
CashExpensesDB.SubmitChanges();
}
}

Assign nulls to variables referencing your objects, the use GC.Collect(); to force garbage collection. You may need to call it twice in a row to speed non-accessible objects through the process.

Set the object to null, then call:
GC.Collect();
GC.WaitForPendingFinalizers();

Related

Is it possible to clear objects with the get property after calling the class?

this below to sample code;
private ExampleStatus _status;
public ExampleStatus status
{
get
{
if (_status == null) _status = new ExampleStatus();
//if (_status.receivedData) _status.receivedData = false; //this line is incorrect !
return _status;
}
}
public class ExampleStatus
{
public int Id { get; set; }
public string Name { get; set; }
public bool receivedData { get; set; }
//I don't want to use this method
public void Clear()
{
Id = 0;
Name = string.Empty;
receivedData = false;
}
}
int stateType = 0;
void ContinuousLoop(ExampleStatus statusObj)
{
while (true)
{
//I don't want to use the options below
//statusObj.Clear();
//or
//statusObj = new ExampleStatus();
if (stateType == 0)
{
statusObj.Id = 0;
statusObj.Name = "Firs Status";
}
else if (stateType == 1)
{
statusObj.Id = 1;
statusObj.Name = "Second Status";
statusObj.receivedData = true;
}
else if (stateType == 2)
{
statusObj.Id = 2;
statusObj.Name = "Third Status";
}
}
}
void RunThread()
{
var t1 = new Thread(() =>
{
ContinuousLoop(status);
});
t1.Start();
}
Is it possible to set default values ​​without a method or new instance, as shown in the example?
Actually that's why I'm asking this question:
I will use the class I have defined in many places. I will need to add a block of code, such as the Clear method, to every place I use it.
I'm also curious about one more thing. If I assign a new instance every time to reset my objects, does this cause problems in memory?
I know more or less how garbage collections work. However, they say that in practice it does not work as said in theory.
So if I add "IDisposable" to my Class, it would tell the garbage collector: Welcome, I'm a litter. Will you take me too?

MemoryCache seems not to dispose all monitors

I've been working with System.Runtime.Caching.MemoryCache and I'm having some trouble regarding Monitors dispose.
So a little bit of context, I'm trying to implement a cache where items get invalidated from changes on a file by using a custom ChangeMonitor (no I don't wan't to use HostFileChangeMonitor), based on a PubSub architecture.
I have 2 types of inserts on MemoryCache:
One time insert, that is supposed to be removed on monitor changes or expirations:
var monitor = new PubSubMonitor(cacheKey, subscription);
policy.ChangeMonitors.Add(monitor);
policy.SlidingExpiration = slidingSpan;
policy.AbsoluteExpiration = expirationDate;
policy.Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable;
policy.RemovedCallback = (args) => {
if (subscription != null) {
subscription.Dispose();
subscription = null;
}
};
Long live insert, that is supposed to get re-added on monitor changes or by expiration:
var monitor = new PubSubMonitor(cacheKey, subscription);
policy.ChangeMonitors.Add(monitor);
policy.SlidingExpiration = ObjectCache.NoSlidingExpiration;
policy.AbsoluteExpiration = DateTime.UtcNow.AddDays(1);
policy.Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable;
policy.UpdateCallback = EncapsulateListener(cacheKey, invalidationCallback, subscription);
Here is the implementation of EncapsulateListenerCallback:
private CacheEntryUpdateCallback EncapsulateListenerCallback(string originalKey, CacheItemInvalidationCallback invalidationCallback, ISubscription subscription) {
return (args) => {
if (args.RemovedReason == CacheEntryRemovedReason.ChangeMonitorChanged || args.RemovedReason == CacheEntryRemovedReason.Expired) {
args.UpdatedCacheItem = new CacheItem(originalKey, invalidationCallback());
var newPolicy = new CacheItemPolicy();
{ //configurations
var newMonitor = new PubSubMonitor(originalKey, subscription);
newPolicy.ChangeMonitors.Add(newMonitor);
newPolicy.SlidingExpiration = ObjectCache.NoSlidingExpiration;
newPolicy.AbsoluteExpiration = DateTime.UtcNow.AddDays(1);
newPolicy.Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable;
newPolicy.UpdateCallback = EncapsulateListenerCallback(originalKey, invalidationCallback, subscription);
}
args.UpdatedCacheItemPolicy = newPolicy;
} else {
if (subscription != null) {
subscription.Dispose();
subscription = null;
}
}
};
}
And also PubSubMonitor:
public class PubSubMonitor : ChangeMonitor {
private readonly string topic;
private readonly ISubscription subscription;
private volatile bool monitorDisposed = false;
public PubSubMonitor(string topic, ISubscription subscription) : base() {
bool initialized = false;
try {
this.topic = topic;
this.subscription = subscription;
this.subscription.OnMessage += this.InnerOnChange;
initialized = true;
} finally {
InitializationComplete();
if (!initialized) {
Dispose();
}
}
}
private void InnerOnChange(object state) {
lock (this) {
if (!monitorDisposed) {
this.subscription.OnMessage -= this.InnerOnChange;
try {
base.OnChanged(state);
} catch (Exception ex) {
//log the error
}
}
}
}
public override string UniqueId => topic + "[" + Guid.NewGuid().ToString() + "]";
protected override void Dispose(bool disposing) {
lock (this) {
this.subscription.OnMessage -= this.InnerOnChange;
monitorDisposed = true;
}
}
}
ISubscription objects will invoke callbacks registered on OnMessage event to notify for a change.
My problem with this implementation is that sometimes, when my application is unloading a System.NullReferenceException is thrown by MemoryCache with this stack:
at System.Runtime.Caching.MemoryCacheStore.RemoveFromCache(System.Runtime.Caching.MemoryCacheEntry, System.Runtime.Caching.CacheEntryRemovedReason, Boolean)
at System.Runtime.Caching.MemoryCacheStore.Remove(System.Runtime.Caching.MemoryCacheKey, System.Runtime.Caching.MemoryCacheEntry, System.Runtime.Caching.CacheEntryRemovedReason)
at System.Runtime.Caching.MemoryCacheEntry.OnDependencyChanged(System.Object)
at System.Runtime.Caching.ChangeMonitor.OnChangedHelper(System.Object)
at System.Runtime.Caching.ChangeMonitor.OnChanged(System.Object)
at MyNamespace.PubSubMonitor.InnerOnChange(System.Object)
at MyNamespace.FileBasedPubSubSubscription.RaiseOnMessage(MyNamespace.PubSubMessage)
at MyNamespace.FileBasedPubSubSubscription.OnFileEvent(System.String, System.Object, System.IO.FileSystemEventArgs)
at MyNamespace.FileBasedPubSubSubscription+<>c__DisplayClass10_0.<SubscribeTopic>b__0(System.Object, System.IO.FileSystemEventArgs)
at System.IO.FileSystemWatcher.OnChanged(System.IO.FileSystemEventArgs)
at System.IO.FileSystemWatcher.CompletionStatusChanged(UInt32, UInt32, System.Threading.NativeOverlapped*)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
From what I was able to troubleshoot with DotPeek is that my subscription is raising an OnMessage even when the MemoryCache object is already Disposed. That subscription stops raising events when it is disposed (by disposing and disable raising events on FileSystemWatcher), and I do the dispose on update and remove callbacks.
Those lock's on PubSub monitors were added to guarantee that dispose method is not invoked while we're on the base.OnChange().
All items gets removed from cache and their monitors are disposed (from documentation and source code I was able to analyse), but somehow there are Monitors not getting disposed, or some cache item UpdateCallback/RemoveCallback not getting invoked, because file change events are still being raised.
Thanks all

Threads monitoring a Queue<Actions>

I doing a small project to map a network (routers only) using SNMP. In order to speed things up, I´m trying to have a pool of threads responsible for doing the jobs I need, apart from the first job which is done by the main thread.
At this time I have two jobs, one takes a parameter the other doesn´t:
UpdateDeviceInfo(NetworkDevice nd)
UpdateLinks() *not defined yet
What I´m trying to achieve is to have those working threads waiting for a job to
appear on a Queue<Action> and wait while it is empty. The main thread will add the first job and then wait for all workers, which might add more jobs, to finish before starting adding the second job and wake up the sleeping threads.
My problem/questions are:
How to define the Queue<Actions> so that I can insert the methods and the parameters if any. If not possible I could make all functions accept the same parameter.
How to launch the working threads indefinitely. I not sure where should I create the for(;;).
This is my code so far:
public enum DatabaseState
{
Empty = 0,
Learning = 1,
Updating = 2,
Stable = 3,
Exiting = 4
};
public class NetworkDB
{
public Dictionary<string, NetworkDevice> database;
private Queue<Action<NetworkDevice>> jobs;
private string _community;
private string _ipaddress;
private Object _statelock = new Object();
private DatabaseState _state = DatabaseState.Empty;
private readonly int workers = 4;
private Object _threadswaitinglock = new Object();
private int _threadswaiting = 0;
public Dictionary<string, NetworkDevice> Database { get => database; set => database = value; }
public NetworkDB(string community, string ipaddress)
{
_community = community;
_ipaddress = ipaddress;
database = new Dictionary<string, NetworkDevice>();
jobs = new Queue<Action<NetworkDevice>>();
}
public void Start()
{
NetworkDevice nd = SNMP.GetDeviceInfo(new IpAddress(_ipaddress), _community);
if (nd.Status > NetworkDeviceStatus.Unknown)
{
database.Add(nd.Id, nd);
_state = DatabaseState.Learning;
nd.Update(this); // The first job is done by the main thread
for (int i = 0; i < workers; i++)
{
Thread t = new Thread(JobRemove);
t.Start();
}
lock (_statelock)
{
if (_state == DatabaseState.Learning)
{
Monitor.Wait(_statelock);
}
}
lock (_statelock)
{
if (_state == DatabaseState.Updating)
{
Monitor.Wait(_statelock);
}
}
foreach (KeyValuePair<string, NetworkDevice> n in database)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(n.Value.Name + ".txt")
{
file.WriteLine(n);
}
}
}
}
public void JobInsert(Action<NetworkDevice> func, NetworkDevice nd)
{
lock (jobs)
{
jobs.Enqueue(item);
if (jobs.Count == 1)
{
// wake up any blocked dequeue
Monitor.Pulse(jobs);
}
}
}
public void JobRemove()
{
Action<NetworkDevice> item;
lock (jobs)
{
while (jobs.Count == 0)
{
lock (_threadswaitinglock)
{
_threadswaiting += 1;
if (_threadswaiting == workers)
Monitor.Pulse(_statelock);
}
Monitor.Wait(jobs);
}
lock (_threadswaitinglock)
{
_threadswaiting -= 1;
}
item = jobs.Dequeue();
item.Invoke();
}
}
public bool NetworkDeviceExists(NetworkDevice nd)
{
try
{
Monitor.Enter(database);
if (database.ContainsKey(nd.Id))
{
return true;
}
else
{
database.Add(nd.Id, nd);
Action<NetworkDevice> action = new Action<NetworkDevice>(UpdateDeviceInfo);
jobs.Enqueue(action);
return false;
}
}
finally
{
Monitor.Exit(database);
}
}
//Job1 - Learning -> Update device info
public void UpdateDeviceInfo(NetworkDevice nd)
{
nd.Update(this);
try
{
Monitor.Enter(database);
nd.Status = NetworkDeviceStatus.Self;
}
finally
{
Monitor.Exit(database);
}
}
//Job2 - Updating -> After Learning, create links between neighbours
private void UpdateLinks()
{
}
}
Your best bet seems like using a BlockingCollection instead of the Queue class. They behave effectively the same in terms of FIFO, but a BlockingCollection will let each of your threads block until an item can be taken by calling GetConsumingEnumerable or Take. Here is a complete example.
http://mikehadlow.blogspot.com/2012/11/using-blockingcollection-to-communicate.html?m=1
As for including the parameters, it seems like you could use closure to enclose the NetworkDevice itself and then just enqueue Action instead of Action<>

Threads conflict in some cases, in loop conditions

I am working on a project that uses Threads. In some cases, I have these problems:
Here is some piece of my code :
List<EmailAddress> lstEmailAddress = new List<EmailAddress>();
private void TimerCheckInternetConnection_Tick(object sender, EventArgs e)
{
lock (TicketLock)
{
if (UtilityManager.CheckForInternetConnection())
{
if (ApplicationRunStatus == Enum_ApplicationRunStatus.UnknownDisconnect || ApplicationRunStatus == Enum_ApplicationRunStatus.IsReady)
{
// Connect
ThreadPool.QueueUserWorkItem((o) =>
{
for (int i = 0; i < lstEmailAddress.Count; i++)
{
lstEmailAddress[i].IsActive = lstEmailAddress[i].Login();
}
this.BeginInvoke(new Action(() =>
{
// some code
}));
});
}
}
}
}
and this is EmailAddress class :
class EmailAddress
{
private Imap4Client imap = new Imap4Client();
private object objectLock = new object();
public bool IsActive;
public string Address;
public string Password;
public string RecieveServerAddress;
public int RecieveServerPort;
public bool Login()
{
lock (objectLock)
{
try
{
imap.ConnectSsl(RecieveServerAddress, RecieveServerPort);
}
catch (Exception)
{
}
try
{
imap.Login(Address, Password);
return true;
}
catch (Exception)
{
return false;
}
}
}
}
And my problem is this:
When I want to use Login procedure that belongs to EmailAddress Class, it has some conflict. As you can see, I used Lock but any thing changed.
For more details:
If I have 3 items in lstEmailAddress , the Login procedure has to be called 3 times by this code. but every time, the login procedure will work on same username and password. So all my emails cannot login correctly.
If I remove threadpool, it will be ok.
Your code is very confusing:
If you add the lock in your code, it will run synchroniously, only one thread at the time, which will lead to performance loss.
If you queue work via QueueUserWorkItem - it will run in other thread, and not inside TicketLock
You should incapsulate locks inside your class, and should not lock entire logic in your program.
You start work for a loop variable i, which is being closured for it's last value, which lead for a problem you state in last sentence.
lock object in Email class isn't static so it's being created for each instance, and doesn't actually lock anithing.
As you are using Invoke method, your code is being started from UI, and you need to pass the synchronization context. I suggest you to use TPL code for this, and do not directly work with ThreadPool
So I suggest you this solution:
List<EmailAddress> lstEmailAddress = new List<EmailAddress>();
private void TimerCheckInternetConnection_Tick(object sender, EventArgs e)
{
// remove this lock as we have another in Email class
//lock (TicketLock)
if (UtilityManager.CheckForInternetConnection())
{
if (ApplicationRunStatus == Enum_ApplicationRunStatus.UnknownDisconnect
|| ApplicationRunStatus == Enum_ApplicationRunStatus.IsReady)
{
for (int i = 0; i < lstEmailAddress.Count; i++)
{
// use local variable to store index
int localIndex = i;
// Connect
ThreadPool.QueueUserWorkItem((o) =>
{
// if you add a lock here, this will run synchroniosly,
// and you aren't really need the ThreadPool
//lock (TicketLock)
lstEmailAddress[localIndex].IsActive = lstEmailAddress[localIndex].Login();
this.BeginInvoke(new Action(() =>
{
// some code
}));
});
}
}
}
}
class EmailAddress
{
// if you have to login only for one user simultaneosly
// use static variables here, other wise simply remove the lock as it is useless
private static Imap4Client imap;
private static object objectLock;
// static constructor for only one initialization for a static fields
static EmailAddress()
{
objectLock = new object();
imap = new Imap4Client();
}
public bool IsActive;
public string Address;
public string Password;
public string RecieveServerAddress;
public int RecieveServerPort;
public bool Login()
{
// aquire a static lock
lock (objectLock)
{
try
{
imap.ConnectSsl(RecieveServerAddress, RecieveServerPort);
}
catch (Exception)
{
// STORE THE EXCEPTION!!!
// return as you haven't connected
return false;
}
try
{
imap.Login(Address, Password);
return true;
}
catch (Exception)
{
// STORE THE EXCEPTION!!!
return false;
}
}
}
}
Change your Code as and try . you code is queing item from lstEmailAddress where it will always go and hit last item from the list. change your code to inquie each item in threadpool. that should fix. it.
for (int i = 0; i < lstEmailAddress.Count; i++)
{
ThreadPool.QueueUserWorkItem((o) =>
{
lstEmailAddress[i].IsActive = lstEmailAddress[i].Login();
}
}

Why does this not work?

I have a class which will act as variables to store data from textboxes:
public class Business
{
Int64 _businessID = new Int64();
int _businessNo = new int();
string _businessName;
string _businessDescription;
public Int64 BusinessID
{
get { return Convert.ToInt64(_businessID.ToString()); }
}
public int BusinessNo
{
get { return _businessNo; }
set { _businessNo = value; }
}
public string BusinessName
{
get { return _businessName; }
set { _businessName = value; }
}
public string BusinessDescription
{
get { return _businessDescription; }
set { _businessDescription = value; }
}
I then have the code to store the data from the textbox into a session and into a list (there can be many businesses uploaded to the database at one time) - database irrelevent for now. I then want to display the list of businesses stored into the session onto the gridview: (b = class business)
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
businessCollection.Add(b);
GridView1.DataSource = GetBusiness();
GridView1.DataBind();
}
It doesn't seem to add the list to the gridview, can someone help?
Debug your code and ensure that if (Session["Business"] != null) actually evaluates to true.
If it is false then you are adding to a list that is never returned from GetBusinesss
Without any more information you can rewrite it like this:
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
return businessCollection;
else
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
I personally wouldn't do it like this, as it seems like you need an assignment to Session["Business"] but I don't want to change the logic of your code.
Update
I wanted to update this with what I think you wanted to accomplish.
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
Session["Business"] = new List<Business>();
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
It seems you are not assigning anything to Session["Business"]
There's a very strong chance that you're problem is caused by the fact that you are referencing the Business List object inconsistently. You've created an accessor for this object, so use it everywhere.
This:
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Should be:
var businessCollection = GetBusiness();
Note the use of var: I suspect defining businessCollection as a member variable is part of the problem. In any case it is bad design if your intent is to store the list in the session. So I would also remove the member declaration for businessCollection and stick with a locally scoped variable.

Categories