4

I have a class B from which I produce a huge number of instances to speed up some searching in an optimization problem. The number gets so large that I frequently produce OutOfMemory-Exceptions. As workaround, I reduce the number of instances every x seconds, but I would like to do something more sensible. For that I would like to know:

  1. What is a good way to manage the number of instances "alive" (created and not yet garbage collected)

  2. More technical: How can I estimate the RAM I have to use (say) about half of it for my instances?

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • I think we need more info on what the instances do to help you. – OneFineDay Aug 11 '13 at 17:29
  • I have a lot of trees with store possible cost-effective paths in a graph. My "class B" is a node of that tree. As my cost function always changes, every tree adds new paths all the time. Old paths may be useful as well but some of the have to be erased due to the described memory reasons. – J Fabian Meier Aug 11 '13 at 17:39
  • 1
    `OutOfMemoryException` is not so common, which indicates that something you're doing terribly wrong. some code may help – Sriram Sakthivel Aug 11 '13 at 17:50
  • Do you really need this many objects for your problem? – William Morrison Aug 11 '13 at 18:09
  • I have about 20000 "shipments" that each have millions of possible paths. The costs change slightly all the time, so it is useful to store shortest and near-shortest paths for the next path finding of that object. This works pretty well for about 10000 shipments, but I lack memory for 20000. (I know this is a short explanation, but it is a difficult problem of logistics optimization) – J Fabian Meier Aug 11 '13 at 18:16

5 Answers5

5

First of all, I would try to reduce the memory footprint of every single object as much as possible. Since you create huge numbers of objects, it is likely that many of them share similar properties, which renders them a perfect candidate for the flyweight pattern. A classical example according to the Wikipedia article is word processing:

A classic example usage of the flyweight pattern is the data structures for graphical representation of characters in a word processor. It might be desirable to have, for each character in a document, a glyph object containing its font outline, font metrics, and other formatting data, but this would amount to hundreds or thousands of bytes for each character. Instead, for every character there might be a reference to a flyweight glyph object shared by every instance of the same character in the document; only the position of each character (in the document and/or the page) would need to be stored internally.

As a second step, I would estimate the size of a single object. I emphasize estimating in this context because getting the actual size is not that easy in C#. This estimate can then be used to the set a maximum number N of objects you can safely instantiate without encountering an OutOfMemoryException.

You can make use of this information by tracking how many objects are (approximately) alive by updating an object counter every time an object is created or destructed, e.g.

class Foo {

    private static NumberOfInstances = 0;

    public Foo() {
        NumberOfInstances++;
    }

    ~Foo() {
        NumberOfInstances--;
    }
}

If thread-safety is an issue, this implementation would of course have to be refined a little.

Edit: As mike z pointed out in his comment, implementing this via a finalizer may cause severe performance problems in this context. Consequently, it would probably be better to implement IDisposable and perform the decrement in the Dispose operation. However, this has the disadvantage that it is possible to forget disposing the object. However, I doubt that this will be a serious issue in your case.

Community
  • 1
  • 1
bigge
  • 1,488
  • 15
  • 27
  • 1
    Destructors can have serious performance implications since objects with destructors require at least 2 GC cycles to be removed from memory. – Mike Zboray Aug 11 '13 at 22:38
2

I don't know the answer to the second question, but the answer to the first question could be:

Alex Siepman
  • 2,499
  • 23
  • 31
2

Sounds like you would like to keep as much of the already calculated data as possible to access it later on. Maybe the MemoryCache class introduced in .NET 4.0 can be helpful in your case.

You could do something like this:

var cache = new MemoryCache("PathCache", new NameValueCollection()
{
  { "CacheMemoryLimitMegabytes", "256" }, // max 256 MB
  { "PhysicalMemoryLimit", "50" } // max 50% of RAM
});

// cache an item
cache["MyPath"] = "...";

// check, whether the cache contains an item
if (cache.Contains("MyPath"))
{
  // cache hit!
  var cachedPath = cache["MyPath"];
}

// ensure cache is released somewhere in your code
cache.Dispose();
Gene
  • 4,192
  • 5
  • 32
  • 56
0

Each node expansion should query the data, and don't store all objects data in memory, just store the defining parameters with each node. As a node is selected show the data from the query.

OneFineDay
  • 9,004
  • 3
  • 26
  • 37
0

You could make use of the factory pattern to create your instances and so keep track of them and manage you're memmory

lordkain
  • 3,061
  • 1
  • 13
  • 18