none
weak reference but not too weak RRS feed

  • Question

  • Hello

    I need to deal with a large quantity of data ( list of items) each item is a class with large data  .

    In order to prevent any RAM problem i want to make a weak reference  to each items.

    I want to implement a weak reference that disposes an item only if 80-90 % of the Ram is used otherwise

    it leaves the objects in memory. 

    If I just implement a weak reference most of the data is discharged from the RAM even if there are still plenty of RAM space.

    Is there a simple way to acheave this without introducing eccessive overhead?

     




    Thursday, April 7, 2011 3:46 PM

Answers

  • Is there a simple way to acheave this without introducing eccessive overhead?

    Hi.

    No, there is no simple way.

    But let me offer a way with some overhead. May be this overhead is acceptable in your case.

    Here we have some kind of cache. This cache uses string keys for it`s data. We use a memory limit for current process. This memory limit is supplied in cache constructor. Background thread is used to check memory usage every 1 second and, depending on check result, either makes all references weak or strong.

    Provided example creates new 64MB-sized object every 100 milliseconds and inserts it into cache.

    Use "Output" window in Visual Studio to see what happens with cache.

     

     

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading;
    
    namespace SemiWeakReferenceExample
    {
      class Program
      {
        static void Main(string[] args)
        {
          const long maxCurrentProcessMemoryUsage = 1024 * 1024 * 256;
          var cache = new SemiWeakCache<LargeDataObject>(maxCurrentProcessMemoryUsage);
          Console.WriteLine("Press Enter to stop...");
    
          int nextKeyNumber = 0;
          while (true)
          {
            if (Console.KeyAvailable && Console.Read() == 13)
              break;
    
            Thread.Sleep(100);
    
            string key = "Key_" + (++nextKeyNumber);
            cache.SetObjectWithKey(key, new LargeDataObject());
            GC.Collect();
          }
    
          cache.Dispose();
        }
      }
    
      /// <summary>
      /// Provides cache that is sensitive for current process memory usage.
      /// </summary>
      /// <typeparam name="T">Type of cache data.</typeparam>
      public sealed class SemiWeakCache<T> : IDisposable
        where T : class
      {
        readonly long MemoryLimit;
    
        readonly List<string> Keys = new List<string>();
    
        readonly List<object> Values = new List<object>();
    
        readonly TimeSpan MemoryCheckTimeout = TimeSpan.FromSeconds(1);
    
        Thread BackgroundThread;
    
        bool IsLowMemoryMode;
    
        /// <summary>
        /// Craetes an instance of cache.
        /// </summary>
        /// <param name="currentProcessMemoryLimit">Max limit of memory for current process.</param>
        public SemiWeakCache(long currentProcessMemoryLimit)
        {
          MemoryLimit = currentProcessMemoryLimit;
    
          BackgroundThread = new Thread(BackgroundProc);
          BackgroundThread.Start();
        }
    
        /// <summary>
        /// Returns a cached object for supplied key.
        /// </summary>
        /// <param name="key">Key for cached object.</param>
        /// <param name="data">Cached object </param>
        /// <returns>True, if key exists in cache, otherwise false.</returns>
        public bool TryGetObjectByKey(string key, out T data)
        {
          if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
    
          lock (Keys)
          {
            var index = Keys.IndexOf(key);
            if (index == -1)
            {
              data = default(T);
              return false;
            }
    
            var item = Values[index];
            var weak = item as WeakReference;
            if (weak != null)
            {
              data = (T)weak.Target;
              if (!weak.IsAlive)
              {
                Keys.RemoveAt(index);
                Values.RemoveAt(index);
                return false;
              }
    
              return true;
            }
    
            data = (T)item;
            return true;
          }
        }
    
        /// <summary>
        /// Inserts an object into cache.
        /// </summary>
        /// <param name="key">Object key.</param>
        /// <param name="data">Object data.</param>
        public void SetObjectWithKey(string key, T data)
        {
          if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
    
          var value = IsLowMemoryMode ? (object)new WeakReference(data) : data;
          lock (Keys)
          {
            var index = Keys.IndexOf(key);
            if (index == -1)
            {
              Keys.Add(key);
              Values.Add(value);
            }
            else
            {
              Values[index] = value;
            }
          }
        }
    
        void BackgroundProc()
        {
          while (true)
          {
            Thread.Sleep(MemoryCheckTimeout);
    
            var currentMemoryUsage = Process.GetCurrentProcess().PrivateMemorySize64;
    
            bool isLowMemoryMode = currentMemoryUsage >= MemoryLimit;
            if (isLowMemoryMode == IsLowMemoryMode)
              continue;
    
            IsLowMemoryMode = isLowMemoryMode;
            Trace.WriteLine("IsLowMemoryMode turned " + (IsLowMemoryMode ? "on" : "off") + ".");
    
            lock (Keys)
            {
              for (int i = 0; i < Values.Count; i++)
              {
                var item = Values[i];
                var weak = item as WeakReference;
                if (isLowMemoryMode)
                {
                  if (weak == null)
                  {
                    Values[i] = new WeakReference(item);
                  }
                }
                else
                {
                  if (weak != null)
                  {
                    var value = weak.Target;
                    if (weak.IsAlive)
                    {
                      Values[i] = value;
                    }
                    else
                    {
                      Keys.RemoveAt(i);
                      Values.RemoveAt(i);
                    }
                  }
                }
              }
            }
          }
        }
    
        /// <summary>
        /// Stops background thread.
        /// </summary>
        public void Dispose()
        {
          var thread = BackgroundThread;
          BackgroundThread = null;
    
          if (thread != null && thread.IsAlive)
          {
            thread.Abort();
            thread.Join();
          }
        }
      }
    
      /// <summary>
      /// Some large object (64 MB) that implements IDisposable.
      /// </summary>
      public sealed class LargeDataObject : IDisposable
      {
        public readonly byte[] Data = new byte[64 * 1024 * 1024]; // 64 MB
    
        bool IsDisposed;
    
        ~LargeDataObject()
        {
          if (!IsDisposed)
          {
            IsDisposed = true;
            Dispose(false);
          }
        }
    
        public void Dispose()
        {
          if (!IsDisposed)
          {
            IsDisposed = true;
            Dispose(true);
            GC.SuppressFinalize(this);
          }
        }
    
        void Dispose(bool disposing)
        {
          Trace.WriteLine("LargeDataObject.Dispose(" + disposing + ")");
        }
      }
    }
    
    

     

     

    Hope, this helps.

    Wednesday, April 20, 2011 7:56 PM
  • This VB also works.

     

    Imports System.Diagnostics
    
    Class MainWindow
      Dim ramCounter As New System.Diagnostics.PerformanceCounter("Memory", "Available MBytes")
    
    
      Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        MessageBox.Show(ramCounter.NextValue.ToString)
      End Sub
    End Class
     

    Wednesday, April 20, 2011 8:47 AM
    Moderator

All replies

  • asp.net caching maybe.
    Thursday, April 7, 2011 3:53 PM
    Moderator
  • asp.net caching  on a wpf  descktop   application??
    Saturday, April 9, 2011 6:52 AM
  • Run it on a server if you want to share the cached objects.

    Run it on the desktop if you don't.  XP and beyond all have IIS.  asp.net caching will automatically handle pushing stuff out of memory and maybe you re-read em if that happens. 

    !! Correction: you don't need IIS

    Depends what you want this mechanism to do.

    Alternatively.

    You could look into serializing to memory mapped files.  Not sure about how much work would be involved.  Depends on exactly how you want it to work and usually data I need to cache is shared.  So you actually want it stored in one place so as a service changes the data the cache is easily invalidated.


    • Proposed as answer by Sheldon _XiaoModerator Wednesday, April 13, 2011 3:24 AM
    • Edited by Andy ONeillModerator Monday, April 18, 2011 2:57 PM IIS is not required - changed in case it misleads some later reader
    Saturday, April 9, 2011 3:36 PM
    Moderator
  • how do you map a file

     

    Monday, April 11, 2011 5:50 AM
  • Hi marck68,

    Have you got what you need from this thread?

     

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, April 15, 2011 6:18 AM
    Moderator
  • Nope

    I still need a solution for the weakreference. Andy suggestion is interesting but i prefere to work on a weak reference side.

    Friday, April 15, 2011 12:04 PM
  • The memory mapped files would be returned by a windows service.

    This would monitor your criteria on RAM etc and remove objects on some sort of criteria ( maybe you allocate a priority ).

    When you request an object it goes and looks in it's dictionary of cached thingummies.

    If it ain't there it goes and gets it. does some check again and pushes something out of cache.

    ( Pretty much what asp.net caching offers ). 

    If that doesn't cover your requirements, could you explan why not because I'm out of ideas mate.

    Friday, April 15, 2011 12:37 PM
    Moderator
  • I have a class that already implement idisposable.

    I just need to find a way to read the percentage of free RAM memory  to allow the garbage collecors to collect the item or not.

    with just 2 lines of code i solve my problem


    Saturday, April 16, 2011 10:20 AM
  • Performancecountar can get the number of free Mb can't see percentage but maybe Mb would do you?

    http://zamov.online.fr/EXHTML/CSharp/CSharp_927308.html

    http://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter.aspx

     

    Monday, April 18, 2011 11:01 AM
    Moderator
  • Something was niggling at the back of my mind about asp.net caching.

    You don't need to run IIS.

    http://www.codeproject.com/KB/cs/cacheinwinformapps.aspx

    Monday, April 18, 2011 2:55 PM
    Moderator
  • Not sure if you'd consider this lame or not, but here's one approach that might have pretty low overhead:

    Create a "refcount" static counter in your item's class or base class.

    When an item is constructed, increment the counter. When it is finalized, decrement it (and really just do only that, as we all know finalizers are evil).

    When it's time to obtain a reference or weak reference, see if the counter is above a certain threshold and choose a strategy.

     

    Monday, April 18, 2011 11:48 PM
  • That'd work if you only had one class and one size of RAM in all machines and no other processes using RAM.

    Otherwise I see problems.

    Checking how much RAM is available is probably a better approach.

    Tuesday, April 19, 2011 8:01 AM
    Moderator
  • Thanks guys for the advices

     

      I am trying to read the Ram with the following code but i get an error

     

     

    Dim ramCounter As New System.Diagnostics.PerformanceCounter("Memory", "Available MBytes")

    MsgBox(ramCounter.NextValue.ToString)

    the format of the string is wrong

    Tuesday, April 19, 2011 3:26 PM
  • I'm afraid C# is more my thing.

    Nextvalue is a method which expects an empty parameter in C#

     

    So this code worked for me.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Diagnostics;
    
    namespace WpfApplication1
    {
      /// <summary>
      /// Interaction logic for MainWindow.xaml
      /// </summary>
      public partial class MainWindow : Window
      {
        private PerformanceCounter ramCounter = new PerformanceCounter("Memory", "Available MBytes"); 
        public MainWindow()
        {
          InitializeComponent();
    
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
          MessageBox.Show(ramCounter.NextValue().ToString());
        }
      }
    }

    Tuesday, April 19, 2011 6:11 PM
    Moderator
  • the problems is generated here Dim ramCounter As New System.Diagnostics.PerformanceCounter("Memory", "Available MBytes")
    Wednesday, April 20, 2011 5:43 AM
  • This VB also works.

     

    Imports System.Diagnostics
    
    Class MainWindow
      Dim ramCounter As New System.Diagnostics.PerformanceCounter("Memory", "Available MBytes")
    
    
      Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        MessageBox.Show(ramCounter.NextValue.ToString)
      End Sub
    End Class
     

    Wednesday, April 20, 2011 8:47 AM
    Moderator
  • Is there a simple way to acheave this without introducing eccessive overhead?

    Hi.

    No, there is no simple way.

    But let me offer a way with some overhead. May be this overhead is acceptable in your case.

    Here we have some kind of cache. This cache uses string keys for it`s data. We use a memory limit for current process. This memory limit is supplied in cache constructor. Background thread is used to check memory usage every 1 second and, depending on check result, either makes all references weak or strong.

    Provided example creates new 64MB-sized object every 100 milliseconds and inserts it into cache.

    Use "Output" window in Visual Studio to see what happens with cache.

     

     

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading;
    
    namespace SemiWeakReferenceExample
    {
      class Program
      {
        static void Main(string[] args)
        {
          const long maxCurrentProcessMemoryUsage = 1024 * 1024 * 256;
          var cache = new SemiWeakCache<LargeDataObject>(maxCurrentProcessMemoryUsage);
          Console.WriteLine("Press Enter to stop...");
    
          int nextKeyNumber = 0;
          while (true)
          {
            if (Console.KeyAvailable && Console.Read() == 13)
              break;
    
            Thread.Sleep(100);
    
            string key = "Key_" + (++nextKeyNumber);
            cache.SetObjectWithKey(key, new LargeDataObject());
            GC.Collect();
          }
    
          cache.Dispose();
        }
      }
    
      /// <summary>
      /// Provides cache that is sensitive for current process memory usage.
      /// </summary>
      /// <typeparam name="T">Type of cache data.</typeparam>
      public sealed class SemiWeakCache<T> : IDisposable
        where T : class
      {
        readonly long MemoryLimit;
    
        readonly List<string> Keys = new List<string>();
    
        readonly List<object> Values = new List<object>();
    
        readonly TimeSpan MemoryCheckTimeout = TimeSpan.FromSeconds(1);
    
        Thread BackgroundThread;
    
        bool IsLowMemoryMode;
    
        /// <summary>
        /// Craetes an instance of cache.
        /// </summary>
        /// <param name="currentProcessMemoryLimit">Max limit of memory for current process.</param>
        public SemiWeakCache(long currentProcessMemoryLimit)
        {
          MemoryLimit = currentProcessMemoryLimit;
    
          BackgroundThread = new Thread(BackgroundProc);
          BackgroundThread.Start();
        }
    
        /// <summary>
        /// Returns a cached object for supplied key.
        /// </summary>
        /// <param name="key">Key for cached object.</param>
        /// <param name="data">Cached object </param>
        /// <returns>True, if key exists in cache, otherwise false.</returns>
        public bool TryGetObjectByKey(string key, out T data)
        {
          if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
    
          lock (Keys)
          {
            var index = Keys.IndexOf(key);
            if (index == -1)
            {
              data = default(T);
              return false;
            }
    
            var item = Values[index];
            var weak = item as WeakReference;
            if (weak != null)
            {
              data = (T)weak.Target;
              if (!weak.IsAlive)
              {
                Keys.RemoveAt(index);
                Values.RemoveAt(index);
                return false;
              }
    
              return true;
            }
    
            data = (T)item;
            return true;
          }
        }
    
        /// <summary>
        /// Inserts an object into cache.
        /// </summary>
        /// <param name="key">Object key.</param>
        /// <param name="data">Object data.</param>
        public void SetObjectWithKey(string key, T data)
        {
          if (String.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
    
          var value = IsLowMemoryMode ? (object)new WeakReference(data) : data;
          lock (Keys)
          {
            var index = Keys.IndexOf(key);
            if (index == -1)
            {
              Keys.Add(key);
              Values.Add(value);
            }
            else
            {
              Values[index] = value;
            }
          }
        }
    
        void BackgroundProc()
        {
          while (true)
          {
            Thread.Sleep(MemoryCheckTimeout);
    
            var currentMemoryUsage = Process.GetCurrentProcess().PrivateMemorySize64;
    
            bool isLowMemoryMode = currentMemoryUsage >= MemoryLimit;
            if (isLowMemoryMode == IsLowMemoryMode)
              continue;
    
            IsLowMemoryMode = isLowMemoryMode;
            Trace.WriteLine("IsLowMemoryMode turned " + (IsLowMemoryMode ? "on" : "off") + ".");
    
            lock (Keys)
            {
              for (int i = 0; i < Values.Count; i++)
              {
                var item = Values[i];
                var weak = item as WeakReference;
                if (isLowMemoryMode)
                {
                  if (weak == null)
                  {
                    Values[i] = new WeakReference(item);
                  }
                }
                else
                {
                  if (weak != null)
                  {
                    var value = weak.Target;
                    if (weak.IsAlive)
                    {
                      Values[i] = value;
                    }
                    else
                    {
                      Keys.RemoveAt(i);
                      Values.RemoveAt(i);
                    }
                  }
                }
              }
            }
          }
        }
    
        /// <summary>
        /// Stops background thread.
        /// </summary>
        public void Dispose()
        {
          var thread = BackgroundThread;
          BackgroundThread = null;
    
          if (thread != null && thread.IsAlive)
          {
            thread.Abort();
            thread.Join();
          }
        }
      }
    
      /// <summary>
      /// Some large object (64 MB) that implements IDisposable.
      /// </summary>
      public sealed class LargeDataObject : IDisposable
      {
        public readonly byte[] Data = new byte[64 * 1024 * 1024]; // 64 MB
    
        bool IsDisposed;
    
        ~LargeDataObject()
        {
          if (!IsDisposed)
          {
            IsDisposed = true;
            Dispose(false);
          }
        }
    
        public void Dispose()
        {
          if (!IsDisposed)
          {
            IsDisposed = true;
            Dispose(true);
            GC.SuppressFinalize(this);
          }
        }
    
        void Dispose(bool disposing)
        {
          Trace.WriteLine("LargeDataObject.Dispose(" + disposing + ")");
        }
      }
    }
    
    

     

     

    Hope, this helps.

    Wednesday, April 20, 2011 7:56 PM