memory not released WPF - wpf

I have developed a sample WPF application that has one window with one button
When application is opened, if observed from task manager, memory occupied: 12.3 MB
Dim b As Boolean = False
Private lst As List(Of String)
Private Sub Btn_Close(sender As Object, e As RoutedEventArgs)
If b = False Then
If lst Is Nothing Then lst = New List(Of String)
For i As Integer = 0 To 30
lst.Add(Convert.ToBase64String(IO.File.ReadAllBytes("d:\test.txt"))) 'memory increases, test.txt file is a 2MB file
Next
'do some operations with lst object
'memory occupied: 133MB
'now again click the same button, it will go to else case now (cause of the last statement)
Else
lst.Clear()
If MsgBox("GC.Collect()?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
GC.Collect()
'in this case, memory occupied: 13MB
Else
'in this case, memory occupied: 133MB, not cleared
End If
End If
b = Not b
End Sub
Only when GC.Collect() statement is executed memory is released, otherwise the memory stays at 133MB only.
I have requirement in a way when click on any button, a new window opens as showdialog that contains a grid with thousands of records, say: 6000 to 1Lakh, here I do some operations with the selected records (here memory increases) and then closes the window. After closing the window, the memory is not released, I explicitly have to execute the statement GC.Collect()
Is there any thing wrong in the code? or why do I need to explicitly call GC.Collect as CLR will take care of it automatically? (in my application, if I repeat above and don't use GC.Collect, I get outofmemory exception after some time)

There's nothing wrong with your code, and there's nothing wrong with your memory consumption either. There is no guarantee that closing your form will immediately release the memory; indeed, GC will reclaim the memory when it needs to, which is very often not when a developer expects it to.
Garbage collection in .NET fires automatically in response to allocations of memory - that is, when you try to declare an object that doesn't fit in the heap's current Gen0, a garbage collection will occur.
The real question to ask here is: why do you have an issue with the memory staying at 133MB? Is it causing you a problem? Unless you have a specific requirement for this memory to be collected, I'd say don't GC.Collect(), and let the framework work it out for you.
(It's worth noting that calling GC.Collect() manually often has a negative effect on the long-term memory consumption of an application.)

Related

VBA global frame array

unfortunately I was not able to answer my question with the help of Google.
I created a Userform with a frame which contains different controls (Textbox, Combobox, Listbox, Label).
Usually the inputs are saved directly to a workbook after the user presses the OK button.
But there is one special situation, where I want the user to fill in several of these Input Userforms one after another and save all the contents of the Userforms at last.
Therefore I created a global Object-Array where I saved the Input-Frames of each Userform, the user filled in.
At the end I wanted to go through all these frames and its controls and save the contents in one step.
My already existing save function uses a frame as input, so I wanted to loop through my frame array.
Here is my example code:
(conArr is a global Object Array variable)
Public Sub btnOK_Click()
conArrRow = 0
ReDim Preserve conArr(conArrRow)
Set conArr(conArrRow) = frmData.frameDataInput
Unload Me
End Sub
After the Userform is unloaded I get back to the main code.
For Each ctl In conArr(0).Controls
MsgBox ctl.Name
Next
Here I get the error at line: For Each ctl In conArr(0).Controls
Microsoft Visual Basic
Run time error -2147418113 (8000ffff)
Automation error
Catastrophic failure.
I guess the problem occurs because the form with its frame is already unloaded and does not exist anymore. If I put the part of the main code directly to the end of btnOK_Click it works.
I appreciate any help.
Thanks in advance.

AppleScript not looping all numbers when using repeat

I am using this script to close all "Alerts" in my notification bar:
tell application "System Events"
tell process "NotificationCenter"
set numwins to (count windows)
repeat with i from numwins to 1 by -1
click button "Close" of window i
end repeat
end tell
end tell
However this doesn't close them all, even when there are no "Alert without Close button".
try catch didn't help. What's wrong?
I tested your script and it seemed to run correctly on my machine, but there is always a potential for problems when you use a repeat loop on a mutating list: in other words, a list that changes as the repeat loop progresses. Each time you close a window Notification Center changes its window list and updates the properties of the remaining windows; the script can simply lose track. I'm a little surprised it doesn't throw errors when this happens, but...
You can try this code and see if it works. rather than referring to windows by index it repeatedly tries to close the last window, ignoring any errors, and keeps on until the window count is zero or it loops 100 times (that last is to prevent an endless loop in case something goes wrong).
tell application "System Events"
tell process "NotificationCenter"
repeat 100 times
try
tell last window
click button "Close"
end tell
end try
if (count of its windows) = 0 then
exit repeat
end if
end repeat
end tell
end tell
EDIT
Per comments, the above doesn't work quite as advertised, so let's switch it over to AppleScriptObjC:
use framework "Foundation"
property NSUserNotificationCenter : class "NSUserNotificationCenter"
NSUserNotificationCenter's defaultUserNotificationCenter's removeAllDeliveredNotifications()
This seems to do the trick on my machine. Of course, NSUserNotificationCenter is deprecated as of 10.14, so this won't work forever — eventually you'll have to shift over to the notification's framework — but it should work for a few more OS versions.
EDIT 2
Per another comment, anyone working on os 10.14 or later (Mojave and Catalina, to date) can do an equivalent AppleScriptObjC routine using the UserNotifications framework, like so:
use framework "UserNotifications"
set notificationCenter to class "UNUserNotificationCenter"'s currentNotificationCenter
notificationCenter's removeAllDeliveredNotifications()
Note that I've used a slightly different syntax (merely calling class "UNUserNotificationCenter" rather than setting up a property). Both work; the property syntax is only preferable when you need to pass the class to handlers.

Display Database for Clients in Library

I'd like to display the data of my Access database on a separate monitor for the students of a library.
What software or Access tool should I use for this please?
The database should be the only thing the students have access to, meaning they can't access other apps on the computer.
Thank you very much for your time.
Firstly: relying on your Access app being on the separate monitor is NOT AT ALL secure if you need to prevent user access to other applications (for so, so many reasons, but press Windows+E for an example).
Secondly: Microsoft Access will open up on the same monitor on which it was last open. Though I assume you know that and wouldn't be asking if that were suitable to you.
Thirdly: here is VBA code that actually moves the Access application window. It DOES NOT determine where the second monitor is - you have to change the value -1500 according to the screen specs (either manually and hard-coded, or better, Google for some code to determine the position of the second monitor). On my setup, it -1500 happens to end up around the middle of the second monitor which I happen to have on the left (possibly unusual?). If your second monitor is on the right, then I think you'll need a positive value that is greater than the resolution of the fist monitor. It just has to be somewhere within the second screen - not necessary perfectly on the edge because the code then maximises the window. Upshot: you need to experiment with that.
What it does:
Take window out of maximised mode (necessary to move it properly).
Move it such that the top-left corner is somewhere in the second screen.
Maximise the window.
VBA:
Declare Function winapi_ShowWindow Lib "user32" Alias "ShowWindow" (ByVal hwnd As Long, ByVal nCmd As Long) As Long
Declare Function winapi_MoveWindow Lib "user32" Alias "MoveWindow" (ByVal hwin As Long, ByVal X As Long, ByVal Y As Long, ByVal dx As Long, ByVal dy As Long, ByVal fRepaint As Long) As Long
Const SW_SHOWNORMAL = 1
Const SW_SHOWMAXIMIZED = 3
Public Sub MoveAccessWindow
winapi_ShowWindow Access.Application.hWndAccessApp, SW_SHOWNORMAL
winapi_Movewindow Access.Application.hWndAccessApp , -1500, 0, 1000, 1000, True
winapi_ShowWindow Access.Application.hWndAccessApp, SW_SHOWMAXIMIZED
End Sub

WPF app with multiple usercontrols

I Am creating simple WPF test project which contains multiple UserControls(Insteda of Pages).I Am using Switcher Class to navigate between different UserControls.When i navigate to different pages,i have observed that memory consuption keep on increasing on each UserControle Navigationand GC is not invoked.
1.So am i doing something wrong in following code?
2.Which part of the code consuming more memory?
3.Do i need to invoke GC for disposing my UserControls on each new UserControle creation?
If need how can i invoke GC?
public void On_Navigate_Click()
{
UserControle newusercontrole=new UserControle();
DataSet ds = new DataSet();
ds=con.getSome_Datafrom_SQL();//Gets data from SQL via connection class
dataGrid_test.ItemsSource = ds.Tables[0].DefaultView;
Grid.SetColumn(newusercontrole, 1);//dataGrid_test is inside newusercontrole and following is the code to add "this" usercontrol to the main window.
Grid.SetRow(newusercontrole, 1);
Grid.SetZIndex(newusercontrole, 10);
Container.Children.Add(newusercontrole);
}
First off, I must point out that if garbage collection really isn't happening (as you said), it's not your fault and it does not mean you're doing something wrong. It only means that the CLR doesn't think that your system is under memory pressure yet.
Now, to manually invoke a garbage collection cycle anyway, you can use the GC.Collect() static method. If a garbage collection actually starts and your memory consumption is still unreasonably high, this means that you're probably doing something wrong: You're keeping an ever increasing number of unnecessary object references and the garbage collector cannot safely collect those objects. This is a kind of a memory leak.
As far as your code goes, I think that the problem is at the end of the method you posted:
Container.Children.Add(newusercontrole);
This seems to add a new object (on every click) to the collection Container.Children. If this is not removed elsewhere, this is probably the cause of your memory leak. I don't know what the suitable solution would be for your use case (since I don't know exactly how your UI should behave), but you'll likely need to find a way to remove the last UserControle added from the Container.Children. If you can use LINQ, then the methods OfType<T>() and Last() could be of use to find it.
In any case, don't leave the GC.Collect() line in production code. Use it only to force a collection cycle for testing purposes, like this one.

How do I avoid a memory leak with LINQ-To-SQL?

I have been having some issues with LINQ-To-SQL around memory usage. I'm using it in a Windows Service to do some processing, and I'm looping through a large amount of data that I'm pulling back from the context. Yes - I know I could do this with a stored procedure but there are reasons why that would be a less than ideal solution.
Anyway, what I see basically is memory is not being released even after I call context.SubmitChanges(). So I end up having to do all sorts of weird things like only pull back 100 records at time, or create several contexts and have them all do separate tasks. If I keep the same DataContext and use it later for other calls, it just eats up more and more memory. Even if I call Clear() on the "var tableRows" array that the query returns to me, set it to null, and call SYstem.GC.Collect() - it still doesn't release the memory.
Now I've read some about how you should use DataContexts quickly and dispose of them quickly, but it seems like their ought to be a way to force the context to dump all its data (or all its tracking data for a particular table) at a certain point to guarantee the memory is free.
Anyone know what steps guarantee that the memory is released?
A DataContext tracks all the objects it ever fetched. It won't release this until it is garbage collected. Also, as it implements IDisposable, you must call Dispose or use the using statement.
This is the right way to go:
using(DataContext myDC = new DataContext)
{
// Do stuff
} //DataContext is disposed
If you don't need object tracking set DataContext.ObjectTrackingEnabled to false. If you do need it, you can use reflection to call the internal DataContext.ClearCache(), although you have to be aware that since its internal, it's subject to disappear in a future version of the framework. And as far as I can tell, the framework itself doesn't use it but it does clear the object cache.
As Amy points out, you should dispose of the DataContext using a using block.
It seems that your primary concern is about creating and disposing a bunch of DataContext objects. This is how linq2sql is designed. The DataContext is meant to have short lifetime. Since you are pulling a lot of data from the database, it makes sense that there will be a lot of memory usage. You are on the right track, by processing your data in chunks.
Don't be afraid of creating a ton of DataContexts. They are designed to be used that way.
Thanks guys - I will check out the ClearCache method. Just for clarification (for future readers), the situation in which I was getting the memory usuage was something like this:
using(DataContext context = new DataContext())
{
while(true)
{
int skipAmount = 0;
var rows = context.tables.Select(x => x.Dept == "Dept").Skip(skipAmount).Take(100);
//break out of loop when out of rows
foreach(table t in rows)
{
//make changes to t
}
context.SubmitChanges();
skipAmount += rows.Count();
rows.Clear();
rows = null;
//at this point, even though the rows have been cleared and changes have been
//submitted, the context is still holding onto a reference somewhere to the
//removed rows. So unless you create a new context, memory usuage keeps on growing
}
}
I just ran into a similar problem. In my case, helped establish the properties of DataContext.ObjectTrackingEnabled to false.
But it works only in the case of iterating through the rows as follows:
using (var db = new DataContext())
{
db.ObjectTrackingEnabled = false;
var documents = from d in db.GetTable<T>()
select d;
foreach (var doc in documents)
{
...
}
}
If, for example, in the query to use the methods ToArray() or ToList() - no effect

Resources