Hi,

It seems I`ve found a bug. Cached queries stay referenced in Domain when Transaction and Session are disposed. Even worse is that the objects used in query expression stay referenced too. So, once object is used in query, it will live forever.

I wrote minimal app to show this bug:

using System;
using System.Linq;
using Xtensive.Storage;
using Xtensive.Storage.Configuration;

namespace Project1
{
    [Serializable]
    [HierarchyRoot]
    public class MyEntity : Entity
    {
        [Field, Key]
        public int Id { get; private set; }

        [Field( Length = 100 )]
        public string Text { get; set; }
    }

    class MyClass
    {
        public static int ObjectsCount;

        public int EntityId;

        public MyClass()
        {
            ++ObjectsCount;
        }

        ~MyClass()
        {
            --ObjectsCount;
        }

        public static void DumpObjectsCount()
        {
            Console.WriteLine( "Objects count: {0}", ObjectsCount );
        }
    }

    class Program
    {
        static void Main( string[] args )
        {
            var config = DomainConfiguration.Load( "Default" );
            var domain = Domain.Build( config );

            MyClass.DumpObjectsCount();

            Console.WriteLine( "Run test..." );
            Test( domain );

            Console.WriteLine( "\r\nCollect garbage..." );
            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.WriteLine( "\r\nAnd finnaly:" );
            MyClass.DumpObjectsCount();

            if( MyClass.ObjectsCount != 0 ) {
                Console.WriteLine( "\r\nMemory leaks found!" );
            } else {
                Console.WriteLine( "\r\nMemory leaks not found!" );
            }

            Console.ReadKey();
        }

        private static void Test( Domain domain )
        {
            MyClass obj = new MyClass
            {
                EntityId = 10
            };

            MyClass.DumpObjectsCount();

            Console.WriteLine( "\r\nExecute query..." );
            using( Session.Open( domain ) ) {
                using( var transactionScope = Transaction.Open() ) {
                    int count = (from e in Query.All<MyEntity>() where e.Id == obj.EntityId select e).Count();
                    // no memory leaks:
                    //int count = (from e in Query.All<MyEntity>() where e.Id == 10 select e).Count();

                    Console.WriteLine( "Query result: {0}", count );

                    transactionScope.Complete();
                }
            }
        }
    }
}

And there is reference graph made by memory profiling tool: http://img11.imageshost.ru/imgs/100427/988dd258f2/t7b032.png

Graph is made from app snapshoot in:

Console.ReadKey();

This thread was imported from our support forum. The original discussion may contain more detailed answer. Original topic by Konstantin.

asked Apr 27 '10 at 11:22

Editor's gravatar image

Editor
46156156157


One Answer:

This must be normal. Try changing DomainConfiguration.QueryCacheSize to 1 and run one more query after this one (or run > 1024 other queries after the first one) - I suspect the reference must disappear.

DO4 caches compiled RSE queries using LruCache (it's shown in graph), but this cache truly works only when you use either the same RSE query object for multiple times )we use this technique internally), or use our own compiled queries (they're compiled just once, so there is just one RSE query as well). All the other queries simply pass through this cache (may be we'll improve this further).


P.S. I don't remember exact LruCache cleanup policy - i.e. possibly it might keep the references to some objects that must be already thrown out a bit longer then it is really necessary - I know we use this approach with other caches to ensure amorthized (= for a large set of operations) insertion and access time is O(1) there.

So may be you must run more queries to get the first one uncached.


Sorry for disinformation: DomainConfiguration.QueryCacheSize affects on size of compiled queries cache only (i.e. cache used by Query.ExecuteXxx); RSE query cache size is defined as static fields, its value is 1024, so currently you can't change it.

answered Apr 27 '10 at 11:53

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

Konstantin wrote: Ok. Thanks for your answer. Now its clear for me. Well wait for RseQueryCacheSize in future releases.

(Apr 27 '10 at 11:53) Editor Editor's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA