Hi,
 
I attached code containing some tests for memoryleaks. Unlike Lucene.NET 1.4.3, 
the current 2.0 codebase from SVN passes these tests without problems.
 
cheers,
Erich

________________________________

Von: Erich Eichinger [mailto:[EMAIL PROTECTED]
Gesendet: Mi 2007-03-07 21:53
An: [EMAIL PROTECTED]; [EMAIL PROTECTED]
Betreff: RE: [VOTE] Release of Apache Lucene.Net 2.0.0




Hi all,

I attached the patch for 1.4.3-final-004. It fixes a couple of memory-/resource 
leaks and some bugs in GermanStemmer. I have this version in use for over 1 
year now in the "wilderness" and never had any problems (at least with Lucene) 
since then.

Unfortunately I didn't have time to look into 2.0 more detailed yet.

hope this helps,
Erich

> -----Original Message-----
> From: George Aroush [mailto:[EMAIL PROTECTED]
> Sent: Thursday, March 01, 2007 4:00 PM
> To: [EMAIL PROTECTED];
> [EMAIL PROTECTED]
> Subject: RE: [VOTE] Release of Apache Lucene.Net 2.0.0
>
> Hi Erich,
>
> Now that you mentioned the version you are using, I believe
> those are fixed in 2.0 -- not necessarily in the same way as
> your fix.  Can you:
>
> 1) Try Lucene.Net 1.9.1 and see if you are still seeing the
> leaks.  I rather have you try 2.0 but some API's have changed in 2.0.
>
> 2) Share with us the patch you used for 1.4.3 -- others may
> find it useful.
>
> Regards,
>
> -- George Aroush
>
> -----Original Message-----
> From: Erich Eichinger [mailto:[EMAIL PROTECTED]
> Sent: Thursday, March 01, 2007 4:12 AM
> To: [EMAIL PROTECTED];
> [EMAIL PROTECTED]
> Subject: RE: [VOTE] Release of Apache Lucene.Net 2.0.0
>
>
> Hi George,
>
> > The memory and resource leaks that you mentioned, have you observed
> > them or do you think they exist?
>
> Yes, I experienced them in a couple of high loaded
> web-applications and the
> fixes I mentioned took me 2 weeks to find them. TBH I
> experienced them using
> Lucene.NET 1.4.3 but checking the codebase showed, that the
> respective code
> didn't change to 2.0.
>
> > Do you have sample code that demonstrates them? 
> > I ask this because you said "possible".
>
> This is misleading. I said "possible" because the underlying
> IndexInput
> might be a RAMDirectory. In case of FileDirectory (or the
> DBDirectory I
> mentioned in my private email) there is almost certainly a
> leak because of
> unclosed file-handles or db-connections.
> I can write some sample code to show up the leaks if you
> like. Btw: I found
> and fixed the leaks in v1.4.3 using SciTech's ".NET Memory
> Profiler" (don't
> want to ad here, but it has been the only useful tool I found for this
> purpose)
>
> > As for your WeakHashtable suggestion and the use of
> NHibernate, this
> > isn't possible.
>
> Another misunderstanding ;-). I didn't want you to link Lucene.NET to
> NHibernate. I just wanted to point you to an implementation I
> am aware of.
>
>
> > In my opinion, none of those issues are critical to stop
> this release.
>
> Since I have to deal almost 100% with web-applications, for
> me these leaks
> are definitely a showstopper. Depending on the application's traffic I
> experienced OutOfMemoryExceptions every few hours. After
> applying the fixes
> I described, everything is running smooth.
>
> cheers,
> Erich
>
> > -----Original Message-----
> > From: George Aroush [mailto:[EMAIL PROTECTED]
> > Sent: Thursday, March 01, 2007 2:19 AM
> > To: [EMAIL PROTECTED];
> > [EMAIL PROTECTED]
> > Subject: RE: [VOTE] Release of Apache Lucene.Net 2.0.0
> >
> > Hi Erich,
> >
> > In my opinion, none of those issues are critical to stop
> this release.
> >
> > The memory and resource leaks that you mentioned, have you observed
> > them or do you think they exist?  Do you have sample code that
> > demonstrates them?  I ask this because you said "possible".
>  Yes, I am
> > aware of a leak issue during sorting when Lucene.Net is
> compiled using
> > .NET 1.1, but with .NET 2.0, it disappears.
> >
> > As for your WeakHashtable suggestion and the use of
> NHibernate, this
> > isn't possible.  This release is supporting .NET 1.1 so we are
> > somewhat limited with what we have.  No, I can't use NHibernate as
> > it's a 3rd party artifact and none ASF.
> >
> > Regards,
> >
> > -- George
> >
> >
> > -----Original Message-----
> > From: Erich Eichinger [mailto:[EMAIL PROTECTED]
> > Sent: Tuesday, February 27, 2007 9:04 AM
> > To: [EMAIL PROTECTED];
> > [EMAIL PROTECTED]
> > Subject: RE: [VOTE] Release of Apache Lucene.Net 2.0.0
> >
> > Hi,
> >
> > there are some resource leaks that lead to really nasty problems in
> > highloaded webapplications. I summed up the most required changes:
> >
> > *)
> > Index/CompoundFileReader.cs:260:
> > Lucene.Net.Index.Compound.FileReader.CSIndexInput doesn't close
> > underlying IndexInput (possible resource leak)
> >
> >
> > *)
> > Index/TermInfosReader.cs
> > Index/SegmentReader.cs
> >
> > Usage of System.Threading.Thread.GetData()/.SetData() may result in
> > memory leaks in web-applications. Using
> > System.Runtime.Remoting.Messaging.CallContext is a much
> better choice.
> >
> >
> > *)
> > Search/FieldCacheImpl.cs
> >
> > cache should be a "WeakHashtable" instead of Hashtable.
> > Otherwise cached readers can't ever be collected and cause
> > memory/resource leaks.
> >
> > *)
> > FieldSortedHitQueue.cs
> >
> > the same problem as in FieldCacheImpl but with "Comparators" table.
> >
> > Asfaik a possible implementation of a "WeakHashtable" can
> be found in
> > the NHibernate project.
> >
> >
> > cheers,
> > Erich
> >
> >
> > > -----Original Message-----
> > > From: George Aroush [mailto:[EMAIL PROTECTED]
> > > Sent: Tuesday, February 27, 2007 4:06 AM
> > > To: [EMAIL PROTECTED];
> > > [EMAIL PROTECTED]
> > > Subject: [VOTE] Release of Apache Lucene.Net 2.0.0
> > >
> > > Hi folks,
> > >
> > > To follow the proper release process of ASF, please take a
> > moment to
> > > cast your vote to release Lucene.Net 2.0.0.  I have
> placed both the
> > > release candidate of source code and binary
> > > here: http://people.apache.org/~aroush/
> > >
> > > The change history for this release can be found here:
> > > https://svn.apache.org/repos/asf/incubator/lucene.net/trunk/C%
> > > 23/src/HISTORY
> > > .txt
> > >
> > > In addition to Lucene.Net release, this release also
> > contains ported
> > > code in "contrib".
> > >
> > > Please cast your vote on releasing Apache Lucene.Net
> 2.0.0 no later
> > > then this coming Saturday.
> > >
> > > [ ] +1 Approve release
> > > [x] -1 Veto release (please give reason)
> > >
> > > Regards,
> > >
> > > -- George Aroush
> > >
> > >
> >
> >
>
>



/*
 /Search/FieldSortedHitQueue
 
*/
using System;
using System.Collections;
using System.Reflection;
using System.Threading;
using Lucene.Net.Analysis;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using NUnit.Framework;
using FieldInfo=System.Reflection.FieldInfo;

namespace Lucene.Net.Test
{
        using Document = Documents.Document;
        
        /// <summary>
        /// Summary description for MemoryLeakage.
        /// </summary>
        [TestFixture]
        public class MemoryLeakage
        {
                private void Reset()
                {
                        FieldSortedHitQueue_ComparatorsCache = new Hashtable();
                        FieldCache_Cache = new Hashtable();

                        object[] threadDataTable = GetThreadLocalDataSlots();
                        for(int i=0;i<threadDataTable.Length;i++)
                        {
                                threadDataTable[i] = null;
                        }                       
                }
                
                [Test]
                public void TestComparatorCache()
                {
                        Reset();
                        DoSomeIndexWork();
                        GC.Collect();

                        Assert.AreEqual(0, 
FieldSortedHitQueue_ComparatorsCache.Count);
                }

                [Test]
                public void TestFieldCache()
                {
                        Reset();
                        DoSomeIndexWork();
                        GC.Collect();

                        Assert.AreEqual(0, FieldCache_Cache.Count);
                }

                [Test]
                public void TestTermInfosReader()
                {
                        Reset();
                        
                        DoSomeIndexWork();
                        GC.Collect();

                        // Ensure nothing is left on ThreadLocal storage
                        object[] threadDataTable = GetThreadLocalDataSlots();
                        for(int i=0;i<threadDataTable.Length;i++)
                        {
                                Assert.AreEqual(null, threadDataTable[i]);
                        }
                }
                
                private object[] GetThreadLocalDataSlots()
                {
                        // ensure ThreadLocalStorage is initialized
                        LocalDataStoreSlot slot = Thread.AllocateDataSlot();
                        Thread.SetData(slot, null);
                        
                        object localDataStore = 
typeof(Thread).InvokeMember("GetDomainLocalStore"
                                , BindingFlags.Static | BindingFlags.NonPublic 
| BindingFlags.InvokeMethod
                                , null
                                , null
                                , null);
                        object[] dataTable = (object[]) 
localDataStore.GetType().GetField("m_DataTable", BindingFlags.Instance | 
BindingFlags.NonPublic).GetValue(localDataStore);
                        return dataTable;
                }
        
                private IDictionary FieldCache_Cache
                {
                        get
                        {
                                FieldCache fieldCache = 
FieldCache_Fields.DEFAULT;
                                IDictionary cache = (IDictionary) 
fieldCache.GetType().GetField("cache", BindingFlags.Instance | 
BindingFlags.NonPublic).GetValue(fieldCache);
                                return cache;
                        }
                        set
                        {
                                FieldCache fieldCache = 
FieldCache_Fields.DEFAULT;
                                fieldCache.GetType().GetField("cache", 
BindingFlags.Instance | BindingFlags.NonPublic).SetValue(fieldCache, value);
                        }
                }
                
                private IDictionary FieldSortedHitQueue_ComparatorsCache
                {
                        get
                        {
                                Type tQueue = 
typeof(IndexSearcher).Assembly.GetType("Lucene.Net.Search.FieldSortedHitQueue");
                                FieldInfo fComparators = 
tQueue.GetField("Comparators", BindingFlags.Static | BindingFlags.NonPublic);
                                return (IDictionary) 
fComparators.GetValue(null);
                        }
                        set
                        {
                                Type tQueue = 
typeof(IndexSearcher).Assembly.GetType("Lucene.Net.Search.FieldSortedHitQueue");
                                FieldInfo fComparators = 
tQueue.GetField("Comparators", BindingFlags.Static | BindingFlags.NonPublic);
                                fComparators.SetValue(null, value);
                        }
                }
                
                #region Test Index Directory Generation
                
                private void DoSomeIndexWork()
                {
                        Searcher searcher = this.GetIndex(true, true);
                        Hits docs = searcher.Search(new TermQuery(new 
Term("contents", "b")), new Sort("string", true));
                        for(int i=0;i<docs.Length();i++)
                        {
                                docs.Doc(i).SetBoost(1.0f);                     
        
                        }
                        searcher.Close();
                }
                
                // document data:
                // the tracer field is used to determine which document was hit
                // the contents field is used to search and sort by relevance
                // the int field to sort by int
                // the float field to sort by float
                // the string field to sort by string
                // the i18n field includes accented characters for testing 
locale-specific sorting
                private System.String[][] data = new System.String[][]{
                                                                                
                                                  // tracer  contents         
int            float           string   custom   i18n
                                                                                
                                                  new System.String[]{   "A",   
"x a",           "5",           "4f",           "c",     "A-3",   "p\u00EAche"},
                                                                                
                                                  new System.String[]{   "B",   
"y a",           "5",           "3.4028235E38", "i",     "B-10",  "HAT"},
                                                                                
                                                  new System.String[]{   "C",   
"x a b c",       "2147483647",  "1.0",          "j",     "A-2",   
"p\u00E9ch\u00E9"},
                                                                                
                                                  new System.String[]{   "D",   
"y a b c",       "-1",          "0.0f",         "a",     "C-0",   "HUT"},
                                                                                
                                                  new System.String[]{   "E",   
"x a b c d",     "5",           "2f",           "h",     "B-8",   "peach"},
                                                                                
                                                  new System.String[]{   "F",   
"y a b c d",     "2",           "3.14159f",     "g",     "B-1",   "H\u00C5T"},
                                                                                
                                                  new System.String[]{   "G",   
"x a b c d",     "3",           "-1.0",         "f",     "C-100", "sin"},
                                                                                
                                                  new System.String[]{   "H",   
"y a b c d",     "0",           "1.4E-45",      "e",     "C-88",  "H\u00D8T"},
                                                                                
                                                  new System.String[]{   "I",   
"x a b c d e f", "-2147483648", "1.0e+0",       "d",     "A-10",  "s\u00EDn"},
                                                                                
                                                  new System.String[]{   "J",   
"y a b c d e f", "4",           ".5",           "b",     "C-7",   "HOT"},
                                                                                
                                                  new System.String[]{   "W",   
"g",             "1",           null,           null,    null,    null},
                                                                                
                                                  new System.String[]{   "X",   
"g",             "1",           "0.1",          null,    null,    null},
                                                                                
                                                  new System.String[]{   "Y",   
"g",             "1",           "0.2",          null,    null,    null},
                                                                                
                                                  new System.String[]{   "Z",   
"f g",           null,          null,           null,    null,    null}};
                
                // create an index of all the documents, or just the x, or just 
the y documents
                private Searcher GetIndex(bool even, bool odd)
                {
                        RAMDirectory indexStore = new RAMDirectory();
                        IndexWriter writer = new IndexWriter(indexStore, new 
SimpleAnalyzer(), true);
                        for (int i = 0; i < data.Length; ++i)
                        {
                                if (((i % 2) == 0 && even) || ((i % 2) == 1 && 
odd))
                                {
                                        Lucene.Net.Documents.Document doc = new 
Lucene.Net.Documents.Document();
                                        doc.Add(new Field("tracer", data[i][0], 
Field.Store.YES, Field.Index.NO));
                                        doc.Add(new Field("contents", 
data[i][1], Field.Store.NO, Field.Index.TOKENIZED));
                                        if (data[i][2] != null)
                                                doc.Add(new Field("int", 
data[i][2], Field.Store.NO, Field.Index.UN_TOKENIZED));
                                        if (data[i][3] != null)
                                                doc.Add(new Field("float", 
data[i][3], Field.Store.NO, Field.Index.UN_TOKENIZED));
                                        if (data[i][4] != null)
                                                doc.Add(new Field("string", 
data[i][4], Field.Store.NO, Field.Index.UN_TOKENIZED));
                                        if (data[i][5] != null)
                                                doc.Add(new Field("custom", 
data[i][5], Field.Store.NO, Field.Index.UN_TOKENIZED));
                                        if (data[i][6] != null)
                                                doc.Add(new Field("i18n", 
data[i][6], Field.Store.NO, Field.Index.UN_TOKENIZED));
                                        doc.SetBoost(2); // produce some scores 
above 1.0
                                        writer.AddDocument(doc);
                                }
                        }
                        writer.Optimize();
                        writer.Close();
                        
                        IndexReader reader = IndexReader.Open(indexStore);
                        reader.DeleteDocument(reader.NumDocs()/2);
                        reader.Close();
                        
                        return new IndexSearcher(indexStore);
                }               
                
                #endregion Test Index Directory Generation              
        }
}

Reply via email to