> Does this include your changes to ClosableThreadLocal? No, the old implementation.
DIGY On Fri, Aug 26, 2011 at 1:45 PM, Ayende Rahien <aye...@ayende.com> wrote: > Does this include your changes to ClosableThreadLocal? > > Also, you probably won't see a memory leak in this fashion. We only saw > that in runs that exceeded 24 hours. > > > On Thu, Aug 25, 2011 at 8:40 PM, Digy <digyd...@gmail.com> wrote: > >> ARGH! I should have done this long before.**** >> >> When I run your test application (using Lucene.Net 2.9.4 in trunk) in a >> loop, I see no memory leakage.**** >> >> ** ** >> >> private static void Main(string[] args)**** >> >> {**** >> >> for (int i = 0; i < 10; i++)**** >> >> {**** >> >> Main2(null);**** >> >> Console.WriteLine(">>> MEM: " + GC.GetTotalMemory(true)); >> **** >> >> }**** >> >> }**** >> >> ** ** >> >> DIGY.**** >> >> ** ** >> >> *From:* Ayende Rahien [mailto:aye...@ayende.com] >> *Sent:* Thursday, August 25, 2011 10:23 AM >> *To:* Itamar Syn-Hershko >> *Cc:* Digy; lucene-net-user@lucene.apache.org >> >> *Subject:* Re: [Lucene.Net] Memory leaks**** >> >> ** ** >> >> Itamar,**** >> >> You need to have some way of knowing that the item that you have there has >> a Close method, no?**** >> >> The only method that IDisposable have is Dispose(), IsDisposing and stuff >> like that isn't part of it.**** >> >> ** ** >> >> The CTL need some way to close its values when it is done, but in order to >> be able to do so, we need an interface to call it.**** >> >> A lot of the stuff in Lucene implements Closable, which is basically >> IDisposable**** >> >> ** ** >> >> On Thu, Aug 25, 2011 at 10:18 AM, Itamar Syn-Hershko <ita...@code972.com> >> wrote:**** >> >> Whats wrong in putting a call to Close() in Close() ?**** >> >> ** ** >> >> IDisposable in itself requires explicit request from the client (and can >> be called on dtor), so it is no different from IDisposable in that regard. >> And if you are calling Close() from client code, you might just as well do >> the rest of the cleaning up there as well by calling subsequent Close()-es >> from there.**** >> >> ** ** >> >> The only benefit of IDisposable is for lengthy disposing (the IsDisposing >> or however thats called), and I'm pretty sure we can live without it here. >> **** >> >> ** ** >> >> On Thu, Aug 25, 2011 at 10:09 AM, Ayende Rahien <aye...@ayende.com> >> wrote:**** >> >> The code looks good.**** >> >> What worries me is what happen when I have an object that has CTL that >> reside inside the CTL.**** >> >> That would mean that the second CTL would never be freed.**** >> >> ** ** >> >> Most specifically, look at FieldsReaderLocal, used in SegmentReader to >> hold a FieldsReader.**** >> >> The problem is that FieldsReader itself holds a CloseableThreadLocal >> (fieldsStreamTL).**** >> >> ** ** >> >> Let us consider the case of a SegmentReader that is used in two threads, >> then is closed.**** >> >> Its thread local variables are closed, but nothing tells the FieldsReader >> that _it_ is closed, so the fieldsStreamTL is never closed.**** >> >> The valu*e *will only be cleaned when you the thread is closed, and that >> may never happen (think thread pool threads, which are almost never allowed >> to die).**** >> >> ** ** >> >> That is why I made the thing implement IDisposable, because you have >> multiple levels of CTL.**** >> >> ** ** >> >> On Wed, Aug 24, 2011 at 11:05 PM, Digy <digyd...@gmail.com> wrote:**** >> >> **** >> >> I created a new version of CloseableThreadLocal for 2.9.4g branch which >> passes unit tests and is more similar to java version.**** >> >> I implemented java's "ThreadLocal" class with two different approach. One >> uses Thread.AllocateDataSlot (which is tooo slow) and the other one**** >> >> uses the same technique in current implementation.**** >> >> Propably It will not solve the memory-leakage problem, but It may be a >> starting point to work on.**** >> >> **** >> >> http://people.apache.org/~digy/CloseableThreadLocal.cs**** >> >> http://people.apache.org/~digy/CloseableThreadLocal.java (v 3.0.2)**** >> >> **** >> >> DIGY**** >> >> **** >> >> *From:* Ayende Rahien [mailto:aye...@ayende.com] >> *Sent:* Wednesday, August 24, 2011 4:26 PM >> *To:* Itamar Syn-Hershko >> *Cc:* lucene-net-user@lucene.apache.org; Digy**** >> >> >> *Subject:* Re: [Lucene.Net] Memory leaks**** >> >> **** >> >> That make sense, and explains how it works.**** >> >> Basically, there is no rooted hard reference anywhere to the values. It >> looks like the actual problem is happening when you have instances in a >> thread that is no longer used, the _values_ aren't weak references, and they >> are leaking.**** >> >> **** >> >> 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.**** >> >> **** >> >> **** >> >> **** >> >> **** >> >> **** >> >> **** >> >> **** >> >> **** >> >> **** >> >> **** >> >> ** ** >> >> ** ** >> >> ** ** >> > >