I have a puzzle about singleton mode freeing object memory in C# between in C++;
Here C++ Code:
#include<iostream>
using namespace std;
class Rocket
{
private:
Rocket() { speed = 100; }
~Rocket() {}
static Rocket* ms_rocket;
public:
int speed;
static Rocket*ShareRocket()
{
if (ms_rocket == NULL)
ms_rocket = new Rocket();
return ms_rocket;
}
static void Close()
{
if (ms_rocket != NULL)
delete ms_rocket;
}
};
Rocket *Rocket::ms_rocket = NULL;
int main()
{
Rocket* p = Rocket::ShareRocket();
p->speed = 100;
cout << p->speed << endl;
Rocket::Close();
cout << p->speed << endl;
getchar();
}
When I use Rocket::Close(), the memory space which ms_rocket pointed to will be freed, ms_rocket become a wild pointer, and the second "cout<age<<endl" show is not 100, but when I use C# , I also use Dispose(), but still show 100. here the C# code:
class A : IDisposable
{
public int age;
public A() { }
public void Run()
{
Console.WriteLine("Run");
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
Console.WriteLine("A is release");
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
class B
{
static A a;
private B() { }
public static A Intance
{
get
{
if (a == null)
a = new A();
return a;
}
}
}
class Class1
{
public static void Main(string[] args)
{
A a = B.Intance;
a.age =100;
Console.WriteLine(a.age);
a.Dispose();
A a1 = B.Intance;
Console.WriteLine(a1.age);
Console.Read();
}
}
In C#, I think when I use Dispose(), the memory('a' object in B singleton) will be released, but in the second access, the age value should not be 100, and the static variable 'a' will become like a wild pointer.
Who can tell me why?
In C# Dispose mainly is used to release unmanaged resources as soon as they are not needed and not to free the memory occupied by object itself - it is handled by garbage collector which will free (when GC will decide that it needs to run) it only when it will become unaccessible from so called GC roots (and static variables are one of the GC roots, so B.Intance will hold the reference to this instance of A in the heap).
So first of all to free the memory taken by current instance of A you will need to set B.Instance to null (and wait for GC to run).
Also fundamentals of garbage collection in CLR can be useful.
Related
I'm digging into developing cross-platform code and after doing some research on handling exit codes in Windows and Linux I've pieced together the class below to handle keeping my console application alive. However, upon closure I receive the error:
Process terminated. A callback was made on a garbage collected delegate of type 'Bot!Bot.Extensions.Environment.SignalHandler+SetConsoleCtrlEventHandler::Invoke'.
internal interface ISignalHandler
{
void Set();
void Wait();
void Exit();
}
internal class SignalHandler : ISignalHandler
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
private readonly SetConsoleCtrlHandler _setConsoleCtrlHandler;
private bool _disposed;
public SignalHandler()
{
if (!NativeLibrary.TryLoad("Kernel32", typeof(Library).Assembly, null, out var kernel)) return;
if (NativeLibrary.TryGetExport(kernel, "SetConsoleCtrlHandler", out var handler))
_setConsoleCtrlHandler =
(SetConsoleCtrlHandler) Marshal.GetDelegateForFunctionPointer(handler,
typeof(SetConsoleCtrlHandler));
}
public void Set()
{
if (_setConsoleCtrlHandler == null) Task.Factory.StartNew(UnixSignalHandler);
else _setConsoleCtrlHandler(WindowsSignalHandler, true);
}
public void Wait()
{
_resetEvent.WaitOne();
}
public void Exit()
{
_resetEvent.Set();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void UnixSignalHandler()
{
UnixSignal[] signals =
{
new UnixSignal(Signum.SIGHUP),
new UnixSignal(Signum.SIGINT),
new UnixSignal(Signum.SIGQUIT),
new UnixSignal(Signum.SIGABRT),
new UnixSignal(Signum.SIGTERM)
};
UnixSignal.WaitAny(signals);
Exit();
}
private bool WindowsSignalHandler(WindowsCtrlType signal)
{
switch (signal)
{
case WindowsCtrlType.CtrlCEvent:
case WindowsCtrlType.CtrlBreakEvent:
case WindowsCtrlType.CtrlCloseEvent:
case WindowsCtrlType.CtrlLogoffEvent:
case WindowsCtrlType.CtrlShutdownEvent:
Exit();
break;
default:
throw new ArgumentOutOfRangeException(nameof(signal), signal, null);
}
return true;
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) _resetEvent.Dispose();
_disposed = true;
}
private delegate bool SetConsoleCtrlHandler(SetConsoleCtrlEventHandler handlerRoutine, bool add);
private delegate bool SetConsoleCtrlEventHandler(WindowsCtrlType sig);
private enum WindowsCtrlType
{
CtrlCEvent = 0,
CtrlBreakEvent = 1,
CtrlCloseEvent = 2,
CtrlLogoffEvent = 5,
CtrlShutdownEvent = 6
}
}
From what I can find, _setConsoleCtrlHandler is being collected too soon, but I cannot determine how to prevent that from happening. Even calling GC.KeepAlive(_setConsoleCtrlHandler) shortly after assigning it, it still generates the error.
You need to create a class-scoped variable for your WindowsSignalHandler:
private readonly SetConsoleCtrlEventHandler
_windowsSignalHandler = WindowsSignalHandler;
Then, pass that in to your method call:
_setConsoleCtrlHandler(_windowsSignalHandler, true);
That will ensure that your callback reference doesn't get collected because you are keeping a reference to it in your object.
I have some code which opens a memory mapped file and exposes a ReadOnlySlice<T> for a number of parsing operations. Simplified class code below:
public MemoryMappedViewAccessor Accessor { get; }
public SafeMemoryMappedViewHandle Handle { get; }
public byte* Memory;
private long _size;
public Parser(MemoryMappedFile mappedFile, long offset, long size)
{
_size = size;
Accessor = mappedFile.CreateViewAccessor(offset, _size, MemoryMappedFileAccess.Read);
Handle = Accessor.SafeMemoryMappedViewHandle;
unsafe
{
Handle.AcquirePointer(ref Memory);
}
}
public ReadOnlySlice<T> GetSpan<T>(int offset, int size)
{
return new ReadOnlySpan<T>(chunk.Memory, _size).Slice(offset, size);
}
/* other functions exposing various Slice<T> over this */
Obviously this requires my class to implement IDisposable, but I am unsure as to where the boundary lies between unsafe and safe resources. One reference I found says that SafeMemoryMappedViewHandle is a managed resource and should be disposed as such, but it doesn't mention how this changes with ReleasePointer, or whether ReleasePointer should even be called before Dispose.
The way I see it, the following two Dispose patterns are my options:
Option 1 - Treat everything as managed
private bool _isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
Accessor.Dispose();
Handle.ReleasePointer(); // is this even needed?
Handle.Dispose();
}
_isDisposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
Option 2 - Treat pointer release as unmanaged
private bool _isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
Accessor.Dispose();
}
Handle.ReleasePointer();
Handle.Dispose();
_isDisposed = true;
}
}
~FileChunk()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
In both of these cases I would add a check to my parsing logic to throw an ObjectDisposedException if _isDisposed is true, to avoid UAF bugs and memory corruption.
Which of these is the correct dispose pattern when unsafe pointers are exposed from a SafeMemoryMappedViewHandle? Additionally, is it worth setting the Memory field to null during disposal?
Using the CSCore library, I wrote the code for playing an mp3 file in the class BGM in a seperate file called BGM.cs and the method for playback is BGM.Play("file directory");, which is called in the Form. But somehow I can't manage to get any sound out of it. I've already checked volume, codec and output, and I can't think of anything else that might cause this problem.
This is the code of the class file:
public class BGM
{
public static void Play(string file)
{
using (IWaveSource soundSource = GetSoundSource(file))
{
using (ISoundOut soundOut = GetSoundOut())
{
soundOut.Initialize(soundSource);
soundOut.Volume = 0.8f;
soundOut.Play();
}
}
}
private static ISoundOut GetSoundOut()
{
if (WasapiOut.IsSupportedOnCurrentPlatform)
return new WasapiOut();
else
return new DirectSoundOut();
}
private static IWaveSource GetSoundSource(string file)
{
return CodecFactory.Instance.GetCodec(file);
}
There are actually a couple reasons why your mp3 isn't playing.
The first reason is you haven't specified a device for the sound to play on. The code below gets the first device that can render sound, but that won't always be correct if the user has multiple devices attached to their computer. You'll have to handle that appropriately. The device has to be set on the WasapiOut object.
The second reason is your use of using statements in your Play method. While it's always a good idea to clean up objects that implement IDisposable, you can't always do so immediately. In this case, soundOut.Play() is not a blocking method, which meant that control was exiting the method immediately, causing Dispose() to be called on soundOut and soundSource. This meant that the sound would effectively never be played (maybe it would start for a short moment, but not enough to really hear it). Essentially, you need to hold onto the references and only dispose of them once playback is complete.
Have a look at the AudioPlayerSample for an idea on how to implement a complete solution. My code should get you started.
void Main()
{
using(var player = new BGM(#"D:\Test.mp3"))
{
player.Play();
// TODO: Need to wait here in order for playback to complete
// Otherwise, you need to hold onto the player reference and dispose of it later
Console.ReadLine();
}
}
public class BGM : IDisposable
{
private bool _isDisposed = false;
private ISoundOut _soundOut;
private IWaveSource _soundSource;
public BGM(string file)
{
_soundSource = CodecFactory.Instance.GetCodec(file);
_soundOut = GetSoundOut();
_soundOut.Initialize(_soundSource);
}
public void Play()
{
if(_soundOut != null)
{
_soundOut.Volume = 0.8f;
_soundOut.Play();
}
}
public void Stop()
{
if(_soundOut != null)
{
_soundOut.Stop();
}
}
private static ISoundOut GetSoundOut()
{
if (WasapiOut.IsSupportedOnCurrentPlatform)
{
return new WasapiOut
{
Device = GetDevice()
};
}
return new DirectSoundOut();
}
private static IWaveSource GetSoundSource(string file)
{
return CodecFactory.Instance.GetCodec(file);
}
public static MMDevice GetDevice()
{
using(var mmdeviceEnumerator = new MMDeviceEnumerator())
{
using(var mmdeviceCollection = mmdeviceEnumerator.EnumAudioEndpoints(DataFlow.Render, DeviceState.Active))
{
// This uses the first device, but that isn't what you necessarily want
return mmdeviceCollection.First();
}
}
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
if(_soundOut != null)
{
_soundOut.Dispose();
}
if(_soundSource != null)
{
_soundSource.Dispose();
}
}
_isDisposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
I'm having a memory leak problem, and I'm wondering if anyone can tell me what I'm doing wrong (or what Microsoft bug I missed). Below is a sample application that demonstrates the problem. Call TestCollectTimer.Test() to run the sample.
The problem is, no matter how many "MyTimerData" are created, or how many times GC.Collect() is called, the finalizer of MyTimerData is never called until the application shuts down.
class TestCollectTimer
{
public static void Test()
{
for (int index_A = 0; index_A < 100000; index_A++)
{
MyTimerData mtd = new MyTimerData();
mtd = null;
}
GC.Collect();
Thread.Sleep(2000);
GC.Collect();
Form f = new Form();
f.ShowDialog();
}
}
class MyTimerData
{
public System.Threading.Timer m_timer;
public MyTimerData()
{
this.m_timer = new System.Threading.Timer(
new System.Threading.TimerCallback(this.TimerCall),
null,
System.Threading.Timeout.Infinite,
System.Threading.Timeout.Infinite);
}
~MyTimerData()
{
MessageBox.Show("Collect My Timer Data");
}
public void TimerCall(object o) { }
}
Thankyou for your help
In debug mode, all local variables' scopes are artificially extended to the end of their enclosing methods, so they won't be collected before you've finished inspecting them in the debugger.
Your code works as expected when compiled in Release mode and run with no debugger attached.
You can also just move your for-loop into its own method, and then your timers will be eligible for collection after it returns, even in debug mode.
What about disposing your timer?
class TestCollectTimer
{
public static void Test()
{
for (int index_A = 0; index_A < 100000; index_A++)
{
using(MyTimerData mtd = new MyTimerData())
{
//do your stuff here
}
}
GC.Collect();
Form f = new Form();
f.ShowDialog();
}
}
class MyTimerData : IDisposable
{
public System.Threading.Timer m_timer;
public MyTimerData()
{
this.m_timer = new System.Threading.Timer(
new System.Threading.TimerCallback(this.TimerCall),
null,
System.Threading.Timeout.Infinite,
System.Threading.Timeout.Infinite);
}
public void TimerCall(object o) { }
public void Dispose()
{
Dispose(true);
}
protected void Dispose(bool disposing)
{
m_timer.Dispose();
GC.SuppressFinalize(this);
}
}
You may look the rule CA1001: http://msdn.microsoft.com/en-us/library/ms182172.aspx
I have this code (taken for a very good and friendly web site)
public class B
{
static public A IntA;
}
public class A
{
private int x;
public A(int num)
{
x = num;
}
public void Print()
{
Console.WriteLine("Value : {0}", x);
}
~A()
{
B.IntA = this;
}
}
class RessurectionExample
{
// Ressurection
static void Main()
{
// Create A instance and print its value
A a = new A(50);
a.Print();
// Strand the A object (have nothing point to it)
a = null;
// Activate the garbage collector
GC.Collect();
// Print A's value again
B.IntA.Print();
}
}
It creates an instance of A with the value 50, prints it, strands the created object by setting his only reference to null, activates his Dtor and after being saved at B - prints it again.
Now, the weird thing is that when debugging, when the cursor points to the last line (B.IntA.Print()), the value of the static A member is null, after pressing F10, I get a NullReferenceException BUT the value of the static A member changes to what it should be.
Can anyone explain this phenomenon?
You need a call to GC.WaitForPendingFinalizers. Without this, your destructor won't actually get called in order.
static void Main()
{
// Create A instance and print its value
A a = new A(50);
a.Print();
// Strand the A object (have nothing point to it)
a = null;
// Activate the garbage collector
GC.Collect();
// Add this to wait for the destructor to finish
GC.WaitForPendingFinalizers();
// Print A's value again
B.IntA.Print();
}