Just to make sure we're on the same (code)page - I'm referring to this impl: https://github.com/apache/lucene.net/blob/trunk/src/core/Util/CloseableThreadLocal.cs
On Wed, Aug 24, 2011 at 3:41 PM, Itamar Syn-Hershko <ita...@code972.com>wrote: > I moved this to a private conversation by accident, so back on the mailing > list now. > > There seem to be a fundamental difference between the original Java impl > and Lucene.NET's, which may as well be causing the leak. > > In the Java version ThreadLocal is a weak reference, and a hard reference > is persisted as well along with the thread holding it (using a synchronized > map). This allows to easily identify stale refs - if the thread holding the > weak ref is not alive anymore, the hard ref is removed and the GC can > process it. > > In Lucene.NET's implementation only a static map of weak references is > kept. If I read this right, unlike Java's implementation where hard > references are kept and dereferenced from within the ThreadLocal when the > thread is dead, here there is nothing that will tell the GC to reclaim the > memory referenced by the Weak ref. The Weak-Hard combination that is missing > here is probably whats causing all this mess. > > Ayende's solution which uses explicit disposal is virtually the missing > piece but in different clothing, since it requires changes to the core. If > the .NET version was made to match Java's that probably won't be necessary. > > Before I rewrite CloseableThreadLocal to match the Java implementation - is > there a reason it was made this way? > > > On Wed, Aug 24, 2011 at 10:51 AM, Ayende Rahien <aye...@ayende.com> wrote: > >> Itamar, >> Can you try writing the same code in Java and see if we get the same >> results? >> >> >> On Tue, Aug 23, 2011 at 8:58 PM, Itamar Syn-Hershko >> <ita...@code972.com>wrote: >> >>> How come the Java version doesn't suffer from this issue? if your answer >>> is going to be due to differences with the JVM, then IDisposable may well be >>> our best option here >>> >>> >>> On Tue, Aug 23, 2011 at 8:51 PM, Digy <digyd...@gmail.com> wrote: >>> >>>> Exactly, but I couldn’t find a nice solution yet. Any help welcomed J* >>>> *** >>>> >>>> ** ** >>>> >>>> Btw, Can you open a new issue for this ? **** >>>> >>>> Other people may also want to work on it.**** >>>> >>>> ** ** >>>> >>>> https://issues.apache.org/jira/browse/LUCENENET**** >>>> >>>> ** ** >>>> >>>> Thanks,**** >>>> >>>> ** ** >>>> >>>> DIGY**** >>>> >>>> ** ** >>>> >>>> *From:* Ayende Rahien [mailto:aye...@ayende.com] >>>> *Sent:* Tuesday, August 23, 2011 8:32 PM >>>> >>>> *To:* Digy >>>> *Cc:* Itamar Syn-Hershko >>>> *Subject:* Re: [Lucene.Net] Memory leaks**** >>>> >>>> ** ** >>>> >>>> I think that the main issue is that you have per thread state, but no >>>> way of releasing this per thread state when you are closing the searcher. >>>> **** >>>> >>>> On Tue, Aug 23, 2011 at 7:59 PM, Digy <digyd...@gmail.com> wrote:**** >>>> >>>> Yes, IndexSearcher is expected to be thread safe. **** >>>> >>>> I am not saying, there is a misuse of it. Instead I try to understand >>>> the real cause of it. IDisposable solution seems to hide the real bug. >>>> **** >>>> >>>> **** >>>> >>>> DIGY**** >>>> >>>> **** >>>> >>>> **** >>>> >>>> *From:* Ayende Rahien [mailto:aye...@ayende.com] >>>> *Sent:* Tuesday, August 23, 2011 7:48 PM >>>> *To:* Digy**** >>>> >>>> >>>> *Cc:* Itamar Syn-Hershko >>>> *Subject:* Re: [Lucene.Net] Memory leaks**** >>>> >>>> **** >>>> >>>> Digy,**** >>>> >>>> IndexSearcher is thread safe, right?**** >>>> >>>> From the docs ( >>>> http://lucene.apache.org/java/3_0_1/api/core/org/apache/lucene/search/IndexSearcher.html >>>> ):**** >>>> >>>> *NOTE*: IndexSearcher instances are completely thread safe, meaning >>>> multiple threads can call any of its methods, concurrently. If your >>>> application requires external synchronization, you should *not* synchronize >>>> on the IndexSearcher instance; use your own (non-Lucene) objects >>>> instead.**** >>>> >>>> The problem is that when you use an index searcher in a different thread >>>> then the one you close it on, you are going to leak some memory. **** >>>> >>>> **** >>>> >>>> On Tue, Aug 23, 2011 at 7:35 PM, Digy <digyd...@gmail.com> wrote:**** >>>> >>>> If I change the searching code as below, It works as expected**** >>>> >>>> **** >>>> >>>> for (int i = 0; i < 150; i++)**** >>>> >>>> {**** >>>> >>>> ExecuteInParallel(() =>**** >>>> >>>> {**** >>>> >>>> var searchers = new[]**** >>>> >>>> {**** >>>> >>>> new IndexSearcher(directories[0], true),*** >>>> * >>>> >>>> new IndexSearcher(directories[1], true),*** >>>> * >>>> >>>> };**** >>>> >>>> **** >>>> >>>> {**** >>>> >>>> var topDocs = searchers[0].Search(new >>>> TermQuery(new Term("val", "test")), 15);**** >>>> >>>> for (int j = 0; j < topDocs.totalHits; j++) >>>> **** >>>> >>>> {**** >>>> >>>> searchers[0].Doc(j);**** >>>> >>>> }**** >>>> >>>> topDocs = searchers[1].Search(new >>>> TermQuery(new Term("val", "test")), 15);**** >>>> >>>> for (int j = 0; j < topDocs.totalHits; j++) >>>> **** >>>> >>>> {**** >>>> >>>> searchers[1].Doc(j);**** >>>> >>>> }**** >>>> >>>> TryAddSlot(slotsField, >>>> allSlotsFromAllThreads);**** >>>> >>>> };**** >>>> >>>> **** >>>> >>>> **** >>>> >>>> foreach (var indexSearcher in searchers)**** >>>> >>>> {**** >>>> >>>> indexSearcher.Close();**** >>>> >>>> }**** >>>> >>>> });**** >>>> >>>> }**** >>>> >>>> **** >>>> >>>> **** >>>> >>>> Since IndexSearcher is closed in the thread where caching is done.**** >>>> >>>> **** >>>> >>>> I have to think about it some more but I don’t think that adding >>>> IDisposables to some classes would be a good solution. It is more related >>>> to >>>> threading issues.**** >>>> >>>> **** >>>> >>>> Anyway, I am working on it.**** >>>> >>>> **** >>>> >>>> DIGY**** >>>> >>>> **** >>>> >>>> **** >>>> >>>> *From:* Ayende Rahien [mailto:aye...@ayende.com] >>>> *Sent:* Tuesday, August 23, 2011 6:05 PM >>>> *To:* digy digy >>>> *Cc:* Itamar Syn-Hershko >>>> *Subject:* Re: [Lucene.Net] Memory leaks**** >>>> >>>> **** >>>> >>>> It appears that we need to use searching as well to reproduce this.**** >>>> >>>> Here is another version of the program.**** >>>> >>>> I apologize for the code, but I basically needed to ensure that we would >>>> get something resembling what we have in RavenDB, which is a multi threaded >>>> access pattern.**** >>>> >>>> **** >>>> >>>> On Tue, Aug 23, 2011 at 5:43 PM, digy digy <digyd...@gmail.com> wrote:* >>>> *** >>>> >>>> If I add **** >>>> >>>> GC.Collect(); **** >>>> >>>> after **** >>>> >>>> simpleAnalyzer.Close();**** >>>> >>>> It outputs zeroes. Am I missing something?**** >>>> >>>> **** >>>> >>>> DIGY**** >>>> >>>> **** >>>> >>>> On Tue, Aug 23, 2011 at 5:07 PM, Itamar Syn-Hershko <ita...@code972.com> >>>> wrote:**** >>>> >>>> See attached.**** >>>> >>>> **** >>>> >>>> The key to this problem is multi-threading. In this sample app, some of >>>> the threads do not dispose of their resources properly, even though Close >>>> is >>>> called correctly.**** >>>> >>>> **** >>>> >>>> The suggested patch is confirmed to fix this behavior, but there may be >>>> other pit-falls our use cases didn't reveal.**** >>>> >>>> **** >>>> >>>> Itamar.**** >>>> >>>> **** >>>> >>>> On Tue, Aug 23, 2011 at 4:19 PM, digy digy <digyd...@gmail.com> wrote:* >>>> *** >>>> >>>> Hi Itamar,**** >>>> >>>> **** >>>> >>>> Can you send a simple self-contained test case showing the bug?**** >>>> >>>> **** >>>> >>>> DIGY**** >>>> >>>> **** >>>> >>>> On Tue, Aug 23, 2011 at 2:41 PM, Itamar Syn-Hershko <ita...@code972.com> >>>> wrote:**** >>>> >>>> Hi,**** >>>> >>>> **** >>>> >>>> We recently discovered another memory leak within Lucene.NET, that is >>>> related to LUCENENET-358 (CloseableThreadLocal memory leak in >>>> LocalDataStoreSlot). As it turns out, disposables weren't disposed >>>> properly. >>>> With RavenDB, that caused memory consumption to always grow.**** >>>> >>>> **** >>>> >>>> We ended up modifying some core files to assure proper disposal, and >>>> things are looking better now. We are still testing, and will follow up if >>>> more fixes will be required.**** >>>> >>>> **** >>>> >>>> Here are the relevant diffs in our fork of Lucene.NET:**** >>>> >>>> **** >>>> >>>> >>>> https://github.com/ayende/ravendb/commit/a5031049599968f85e733f22a1ed568c09703c30 >>>> **** >>>> >>>> >>>> https://github.com/ayende/ravendb/commit/bd9ca1ab154732d27985c2d237917af8bbd5fbf3 >>>> **** >>>> >>>> **** >>>> >>>> The full discussion and bug hunting is here:**** >>>> >>>> **** >>>> >>>> >>>> http://groups.google.com/group/ravendb/browse_thread/thread/9c6340987407aece/1d86d6a61175233c >>>> **** >>>> >>>> **** >>>> >>>> I'm also attaching a diff between our fork to the Lucene.Net_2_9_2_RC2 >>>> tag. Will be happy to see this incorporated into 2.9.4 (and for it to be >>>> released already...).**** >>>> >>>> **** >>>> >>>> Itamar.**** >>>> >>>> **** >>>> >>>> **** >>>> >>>> **** >>>> >>>> **** >>>> >>>> **** >>>> >>>> ** ** >>>> >>> >>> >> >