├── docs └── images │ ├── game-of-life.png │ └── dotmemory-screen.png ├── GameOfLife ├── ViewModel │ ├── ITimer.cs │ ├── Cell.cs │ ├── TimerImpl.cs │ ├── PetriDish.cs │ └── MainScreenViewModel.cs ├── App.config ├── App.xaml.cs ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ └── Annotations.cs ├── ComponentContainer.cs ├── View │ ├── SettingsWindow.xaml.cs │ ├── SettingsWindow.xaml │ └── PetriDishControl.cs ├── MainWindow.xaml.cs ├── EmulateExternalLib │ ├── Settings.cs │ └── DelegateCommand.cs ├── Common │ └── NotifyCollection.cs ├── MainWindow.xaml ├── App.xaml └── GameOfLife.csproj ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Tests ├── packages.config ├── ComponentContainerTest.cs ├── Properties │ └── AssemblyInfo.cs ├── PetriDishTest.cs ├── MainScreenViewModelTest.cs ├── Tests.csproj └── SimplestDemoTest.cs ├── DotMemoryDemo.sln.DotSettings ├── DotMemoryDemo.sln ├── README.md └── LICENSE /docs/images/game-of-life.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/dotmemory-demos/HEAD/docs/images/game-of-life.png -------------------------------------------------------------------------------- /docs/images/dotmemory-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/dotmemory-demos/HEAD/docs/images/dotmemory-screen.png -------------------------------------------------------------------------------- /GameOfLife/ViewModel/ITimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GameOfLife.ViewModel 4 | { 5 | public interface ITimer 6 | { 7 | event Action Tick; 8 | } 9 | } -------------------------------------------------------------------------------- /GameOfLife/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /GameOfLife/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace GameOfLife 4 | { 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuget packages 2 | Packages/ 3 | 4 | # Compiling result files 5 | Bin/ 6 | Obj/ 7 | 8 | # Visual Studio and ReSharper files 9 | _ReSharper.Caches/ 10 | .vs/ 11 | *.sdf 12 | *.suo 13 | *.user 14 | 15 | # Rider Files 16 | .idea/ -------------------------------------------------------------------------------- /GameOfLife/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | 3 | This project and the corresponding community is governed by the [JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct). Please make sure you read it. 4 | 5 | -------------------------------------------------------------------------------- /Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DotMemoryDemo.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> -------------------------------------------------------------------------------- /GameOfLife/ViewModel/Cell.cs: -------------------------------------------------------------------------------- 1 | namespace GameOfLife.ViewModel 2 | { 3 | public class Cell 4 | { 5 | private int age; 6 | private bool isAlive; 7 | 8 | public Cell(int age, bool isAlive) 9 | { 10 | this.age = age; 11 | this.isAlive = isAlive; 12 | } 13 | 14 | public int Age 15 | { 16 | get { return age; } 17 | set { age = value; } 18 | } 19 | 20 | public bool IsAlive 21 | { 22 | get { return isAlive; } 23 | set { isAlive = value; } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /GameOfLife/ComponentContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GameOfLife.ViewModel; 3 | 4 | namespace GameOfLife 5 | { 6 | public class ComponentContainer : IDisposable 7 | { 8 | private MainScreenViewModel mainViewModel; 9 | 10 | public MainScreenViewModel CreateMainViewModel() 11 | { 12 | // if(mainViewModel == null) // fix 13 | mainViewModel = new MainScreenViewModel(2); 14 | return mainViewModel; 15 | } 16 | 17 | public void Dispose() 18 | { 19 | mainViewModel.Dispose(); // fix a leak 20 | mainViewModel = null; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /GameOfLife/View/SettingsWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace GameOfLife.View 4 | { 5 | /// 6 | /// Interaction logic for SettingsWindow.xaml 7 | /// 8 | public partial class SettingsWindow 9 | { 10 | public SettingsWindow() 11 | { 12 | InitializeComponent(); 13 | } 14 | 15 | private void OnOk(object sender, RoutedEventArgs e) 16 | { 17 | DialogResult = true; 18 | } 19 | 20 | private void OnCancel(object sender, RoutedEventArgs e) 21 | { 22 | DialogResult = false; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /GameOfLife/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using GameOfLife.View; 3 | 4 | namespace GameOfLife 5 | { 6 | /// 7 | /// Interaction logic for MainWindow.xaml 8 | /// 9 | public partial class MainWindow 10 | { 11 | private readonly ComponentContainer container = new ComponentContainer(); 12 | 13 | public MainWindow() 14 | { 15 | InitializeComponent(); 16 | 17 | var mainScreenViewModel = container.CreateMainViewModel(); 18 | mainScreenViewModel.ShowSettingsView = ShowSettingsView; 19 | DataContext = mainScreenViewModel; 20 | } 21 | 22 | protected override void OnClosed(EventArgs e) 23 | { 24 | container.Dispose(); 25 | } 26 | 27 | private static void ShowSettingsView() 28 | { 29 | var settingsWindow = new SettingsWindow(); 30 | settingsWindow.ShowDialog(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /GameOfLife/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GameOfLife.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GameOfLife/View/SettingsWindow.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Tests/ComponentContainerTest.cs: -------------------------------------------------------------------------------- 1 | using GameOfLife; 2 | using JetBrains.dotMemoryUnit; 3 | using JetBrains.dotMemoryUnit.Kernel; 4 | using NUnit.Framework; 5 | 6 | namespace Tests 7 | { 8 | public class ComponentContainerTest 9 | { 10 | [Test] 11 | public void ShutdownTest() 12 | { 13 | RunAndShutdownApplication(); 14 | 15 | // --assert 16 | dotMemory.Check(memory => 17 | { 18 | Assert.That(memory.GetObjects(@where => @where.Namespace.Like("GameOfLife.*")).ObjectsCount, Is.EqualTo(0)); 19 | }); 20 | } 21 | 22 | /// 23 | /// When code is build in the 'debug' configuration CLR doesn't collect the object till exiting a visibility scope, 24 | /// even if it is not referenced, in contrast with 'release' configuration when object is collected as soon 25 | /// as it is not referenced by any root. 26 | /// This method is need To eliminate this difference and make the test stable by isolating variable 27 | /// 'target' visibility scope. 28 | /// 29 | private static void RunAndShutdownApplication() 30 | { 31 | // --arrange 32 | var target = new ComponentContainer(); 33 | target.CreateMainViewModel(); 34 | 35 | // --act 36 | target.Dispose(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /DotMemoryDemo.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameOfLife", "GameOfLife\GameOfLife.csproj", "{008C1B6C-FB6F-467D-BB70-DDB1F95C29AB}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{8EDB8E61-A7CD-48C8-B484-39AC692307A5}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {008C1B6C-FB6F-467D-BB70-DDB1F95C29AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {008C1B6C-FB6F-467D-BB70-DDB1F95C29AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {008C1B6C-FB6F-467D-BB70-DDB1F95C29AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {008C1B6C-FB6F-467D-BB70-DDB1F95C29AB}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {8EDB8E61-A7CD-48C8-B484-39AC692307A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {8EDB8E61-A7CD-48C8-B484-39AC692307A5}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {8EDB8E61-A7CD-48C8-B484-39AC692307A5}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {8EDB8E61-A7CD-48C8-B484-39AC692307A5}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /GameOfLife/ViewModel/TimerImpl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Timers; 3 | 4 | namespace GameOfLife.ViewModel 5 | { 6 | public class TimerImpl : ITimer 7 | { 8 | private readonly Timer timer; 9 | private int updateOnceIn = 200; 10 | 11 | public TimerImpl() 12 | { 13 | timer = new Timer(updateOnceIn); 14 | timer.Elapsed += TimerOnElapsed; 15 | } 16 | 17 | private void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs) 18 | { 19 | GenerateTick(null); 20 | } 21 | 22 | public void Start() 23 | { 24 | // timer.Change(0, UpdateOnceIn); 25 | timer.Enabled = true; 26 | } 27 | 28 | public void Stop() 29 | { 30 | // timer.Change(-1, UpdateOnceIn); 31 | timer.Enabled = false; 32 | } 33 | 34 | public int UpdateOnceIn 35 | { 36 | get { return updateOnceIn; } 37 | set 38 | { 39 | updateOnceIn = value; 40 | // timer.Change(updateOnceIn, updateOnceIn); 41 | timer.Interval = updateOnceIn; 42 | } 43 | } 44 | 45 | private void GenerateTick(object state) 46 | { 47 | RaiseTick(); 48 | } 49 | 50 | public event Action Tick; 51 | 52 | protected virtual void RaiseTick() 53 | { 54 | var handler = Tick; 55 | if (handler != null) 56 | { 57 | handler(); 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using JetBrains.dotMemoryUnit; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("Tests")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("Tests")] 14 | [assembly: AssemblyCopyright("Copyright © 2016")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | [assembly: Guid("b48f80f9-22bd-453c-90b2-b25f22607edf")] 25 | 26 | // Version information for an assembly consists of the following four values: 27 | // 28 | // Major Version 29 | // Minor Version 30 | // Build Number 31 | // Revision 32 | // 33 | // You can specify all the values or you can default the Build and Revision Numbers 34 | // by using the '*' as shown below: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyVersion("1.0.0.0")] 37 | [assembly: AssemblyFileVersion("1.0.0.0")] 38 | 39 | 40 | [assembly: DotMemoryUnit(FailIfRunWithoutSupport = false)] 41 | 42 | -------------------------------------------------------------------------------- /GameOfLife/EmulateExternalLib/Settings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace ExternalLib 6 | { 7 | public sealed class Settings 8 | { 9 | private readonly DelegateCommand applyCommand; 10 | private readonly DelegateCommand cancelCommand; 11 | 12 | private int origHeight; 13 | private int origWidth; 14 | 15 | public Settings() 16 | { 17 | applyCommand = new DelegateCommand(Apply, () => true); 18 | cancelCommand = new DelegateCommand(Cancel, () => true); 19 | 20 | Width = origWidth = 80; 21 | Height = origHeight = 50; 22 | } 23 | 24 | public static Settings Instance { get; } = new Settings(); 25 | 26 | public int Width { get; set; } 27 | public int Height { get; set; } 28 | 29 | public ICommand ApplyCommand 30 | { 31 | get { return applyCommand; } 32 | } 33 | 34 | public ICommand CancelCommand 35 | { 36 | get { return cancelCommand; } 37 | } 38 | 39 | private void Apply() 40 | { 41 | origWidth = Width; 42 | origHeight = Height; 43 | RaiseSizeChanged(); 44 | } 45 | 46 | private void Cancel() 47 | { 48 | Width = origWidth; 49 | Height = origHeight; 50 | } 51 | 52 | public event EventHandler SizeChanged; 53 | 54 | private void RaiseSizeChanged() 55 | { 56 | var handler = SizeChanged; 57 | if (handler != null) 58 | { 59 | handler(this, EventArgs.Empty); 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /GameOfLife/EmulateExternalLib/DelegateCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | using JetBrains.Annotations; 4 | 5 | // ReSharper disable once CheckNamespace 6 | 7 | namespace ExternalLib 8 | { 9 | public class DelegateCommand : ICommand 10 | { 11 | private readonly Action myExecute; 12 | 13 | private readonly Predicate myCanExecute; 14 | 15 | public DelegateCommand([NotNull] Action execute, Predicate canExecute = null) 16 | { 17 | if (execute == null) 18 | { 19 | throw new ArgumentNullException("execute"); 20 | } 21 | 22 | myExecute = execute; 23 | myCanExecute = canExecute; 24 | } 25 | 26 | public DelegateCommand([NotNull] Action execute, Func canExecute = null) 27 | { 28 | if (execute == null) 29 | { 30 | throw new ArgumentNullException("execute"); 31 | } 32 | 33 | myExecute = _ => execute(); 34 | myCanExecute = canExecute != null 35 | ? _ => canExecute() 36 | : (Predicate) null; 37 | } 38 | 39 | public bool CanExecute(object parameter) 40 | { 41 | return myCanExecute == null || myCanExecute(parameter); 42 | } 43 | 44 | public void Execute(object parameter) 45 | { 46 | myExecute(parameter); 47 | } 48 | 49 | public event EventHandler CanExecuteChanged; 50 | 51 | public void RaiseCanExcuteChanged() 52 | { 53 | var handler = CanExecuteChanged; 54 | if (handler != null) 55 | { 56 | handler(this, EventArgs.Empty); 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /GameOfLife/Common/NotifyCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.Specialized; 5 | 6 | namespace GameOfLife.Common 7 | { 8 | public class NotifyCollection : IEnumerable, INotifyCollectionChanged 9 | { 10 | private const int DefaultCcapacity = 4; 11 | private T[] array = new T[DefaultCcapacity]; 12 | private int capacity = DefaultCcapacity; 13 | 14 | public int Count { get; private set; } 15 | 16 | public IEnumerator GetEnumerator() 17 | { 18 | for (var i = 0; i < Count; i++) 19 | { 20 | yield return array[i]; 21 | } 22 | } 23 | 24 | IEnumerator IEnumerable.GetEnumerator() 25 | { 26 | return GetEnumerator(); 27 | } 28 | 29 | public event NotifyCollectionChangedEventHandler CollectionChanged; 30 | 31 | public void Add(T item) 32 | { 33 | EnsureCapacity(); 34 | array[Count++] = item; 35 | RaiseCollectionChanged(); 36 | } 37 | 38 | public T RemoveLast() 39 | { 40 | --Count; 41 | var last = array[Count]; 42 | array[Count] = default(T); 43 | RaiseCollectionChanged(); 44 | return last; 45 | } 46 | 47 | private void EnsureCapacity() 48 | { 49 | if (Count + 1 > capacity) 50 | { 51 | capacity *= 2; 52 | var newArray = new T[capacity]; 53 | Array.Copy(array, newArray, array.Length); 54 | array = newArray; 55 | } 56 | } 57 | 58 | protected virtual void RaiseCollectionChanged() 59 | { 60 | var handler = CollectionChanged; 61 | if (handler != null) 62 | { 63 | handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /Tests/PetriDishTest.cs: -------------------------------------------------------------------------------- 1 | using GameOfLife.ViewModel; 2 | using JetBrains.dotMemoryUnit; 3 | using NUnit.Framework; 4 | using Rhino.Mocks; 5 | 6 | namespace Tests 7 | { 8 | public class PetriDishTest 9 | { 10 | const int Mb = 1024 * 1024; 11 | 12 | private readonly ITimer timer = MockRepository.GenerateStub(); 13 | 14 | [Test] 15 | [AssertTraffic(AllocatedSizeInBytes = 2 * Mb)] // --assert 16 | public void WholeRunTraffic() 17 | { 18 | // --act 19 | var target = new PetriDish(160, 100, timer); 20 | 21 | for (var i = 0; i < 100; i++) 22 | target.PerformOneStep(); 23 | } 24 | 25 | [Test] 26 | [DotMemoryUnit(CollectAllocations = true)] 27 | public void AlgorithmTraffic() 28 | { 29 | // --arrange 30 | var target = new PetriDish(160, 100, timer); 31 | 32 | var memoryPoint1 = dotMemory.Check(); 33 | 34 | // --act 35 | for (var i = 0; i < 100; i++) 36 | target.PerformOneStep(); 37 | 38 | // --assert 39 | dotMemory.Check(memory => 40 | Assert.That(memory.GetTrafficFrom(memoryPoint1) 41 | .AllocatedMemory 42 | .ObjectsCount, 43 | Is.LessThan(100))); 44 | } 45 | 46 | [Test] 47 | public void DontRecreateArrays() 48 | { 49 | // --arrange 50 | var target = new PetriDish(160, 100, timer); 51 | 52 | var memoryPoint1 = dotMemory.Check(); 53 | 54 | // --act 55 | target.PerformOneStep(); 56 | 57 | // --assert 58 | dotMemory.Check(memory => 59 | Assert.That(memory.GetDifference(memoryPoint1) 60 | .GetNewObjects(where => where.Type.Is()) 61 | .ObjectsCount, 62 | Is.EqualTo(0))); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Tests/MainScreenViewModelTest.cs: -------------------------------------------------------------------------------- 1 | using GameOfLife.ViewModel; 2 | using JetBrains.dotMemoryUnit; 3 | using NUnit.Framework; 4 | 5 | namespace Tests 6 | { 7 | public class MainScreenViewModelTest 8 | { 9 | [Test] 10 | public void RemovePetriDish() 11 | { 12 | // --arrange 13 | using (var target = new MainScreenViewModel(0)) 14 | { 15 | target.AddPetriDishCommand.Execute(null); 16 | 17 | // --act 18 | target.RemovePetriDishCommand.Execute(null); 19 | 20 | // --assert 21 | Assert.That(target.PetriDishesCollection, Has.Count.EqualTo(0)); 22 | 23 | // --assert memory 24 | dotMemory.Check(memory => 25 | Assert.That(memory.GetObjects(where => where. 26 | Type.Is()) 27 | .ObjectsCount, 28 | Is.EqualTo(0))); 29 | } 30 | } 31 | 32 | [Test] 33 | public void NoObjectsLeakedOnEventHandler() 34 | { 35 | // --act 36 | CreateAndReleaseMainScreenViewModel(); 37 | 38 | // --assert 39 | dotMemory.Check(memory => 40 | { 41 | Assert.That( 42 | memory.GetObjects(where => where.Namespace.Like("GameOfLife.*")) // include only objects from namespace "GameOfLife" 43 | .GetObjects(where => where.LeakedOnEventHandler()).ObjectsCount, 44 | Is.EqualTo(0)); 45 | }); 46 | } 47 | 48 | [Test] 49 | public void AllObjectsAreReleased() 50 | { 51 | // --act 52 | CreateAndReleaseMainScreenViewModel(); 53 | 54 | // --assert 55 | dotMemory.Check(memory => 56 | Assert.That( 57 | memory.GetObjects(where => where. 58 | Namespace.Like("GameOfLife.*")) 59 | .ObjectsCount, 60 | Is.EqualTo(0))); 61 | } 62 | 63 | private static void CreateAndReleaseMainScreenViewModel() 64 | { 65 | using (new MainScreenViewModel(2)) 66 | { 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /GameOfLife/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("GameOfLife")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("GameOfLife")] 15 | [assembly: AssemblyCopyright("Copyright © 2016")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![internal JetBrains project](http://jb.gg/badges/internal-flat-square.svg)](https://github.com/JetBrains) 2 | 3 | # DotMemory Demos 4 | 5 | This repository contains a demo application that can be used to demonstrate JetBrains [dotMemory](https://www.jetbrains.com/dotmemory) and [dotMemory Unit](https://www.jetbrains.com/dotmemory). 6 | 7 | The demo application contains various memory issues, such as memory leaks, high memory traffic and so on. It is used in various [screencasts available from our documentation](https://www.jetbrains.com/dotmemory/documentation/). 8 | 9 | ## Demo application 10 | 11 | The demo application is an implementation of [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), where we can experiment with structures that evolve through generations. 12 | 13 | ![Game of Life in action](docs/images/game-of-life.png) 14 | 15 | The solution file contains two projects: 16 | 17 | * **GameOfLife** - The Game of Life implementation, featuring various memory issues. 18 | * **Tests** - Unit tests that use dotMemory Unit to validate memory behaviour. 19 | 20 | ### Profiling 21 | 22 | The Game of Life can be profiled using [dotMemory](https://www.jetbrains.com/dotmemory), with various settings (such as collecting allocations or not). 23 | 24 | ![dotMemory collecting snapshots](docs/images/dotmemory-screen.png) 25 | 26 | There are various issues to be uncovered. Some are easy to find using dotMemory's automatic inspections, while others require a bit more investigation. 27 | 28 | ### Unit testing 29 | 30 | Discovering memory issues is one thing, making sure they will not occur again is another. The tests project contains various unit tests that make use of [dotMemory Unit](https://www.jetbrains.com/dotmemory) and replay profiling sessions in code. These tests will fail when run, as the memory leaks are still in the code base. Throughout the code, there are also comments that help resolve the memory issues. 31 | 32 | For example, this test verifies the amount of memory allocated during execution: 33 | 34 | ```csharp 35 | [Test] 36 | [AssertTraffic(AllocatedSizeInBytes = 2*Mb)] // --assert 37 | public void WholeRunTraffic() 38 | { 39 | // --act 40 | var target = new PetriDish(160, 100, timer); 41 | 42 | for (var i = 0; i < 100; i++) 43 | target.PerformOneStep(); 44 | } 45 | ``` 46 | 47 | Searching for `WholeRunTraffic` will pinpoint the location of the test failure and provide a hint for fixing it. 48 | -------------------------------------------------------------------------------- /GameOfLife/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 |