Created
August 18, 2011 15:52
-
-
Save anonymous/1154378 to your computer and use it in GitHub Desktop.
CloseableThreadLocal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Threading; | |
namespace Lucene.Net.Util | |
{ | |
/// <summary>Java's builtin ThreadLocal has a serious flaw: | |
/// it can take an arbitrarily long amount of time to | |
/// dereference the things you had stored in it, even once the | |
/// ThreadLocal instance itself is no longer referenced. | |
/// This is because there is single, master map stored for | |
/// each thread, which all ThreadLocals share, and that | |
/// master map only periodically purges "stale" entries. | |
/// | |
/// While not technically a memory leak, because eventually | |
/// the memory will be reclaimed, it can take a long time | |
/// and you can easily hit OutOfMemoryError because from the | |
/// GC's standpoint the stale entries are not reclaimaible. | |
/// | |
/// This class works around that, by only enrolling | |
/// WeakReference values into the ThreadLocal, and | |
/// separately holding a hard reference to each stored | |
/// value. When you call {@link #close}, these hard | |
/// references are cleared and then GC is freely able to | |
/// reclaim space by objects stored in it. | |
/// </summary> | |
///<remarks> | |
/// .NET doesn't have this problem, this was adapted to use the .NET version | |
/// </remarks> | |
public class CloseableThreadLocal | |
{ | |
ThreadLocal<object> self ; | |
private string guid; | |
private static readonly ConcurrentDictionary<string, Dump> tracker = new ConcurrentDictionary<string, Dump>(); | |
public CloseableThreadLocal() | |
{ | |
guid = Guid.NewGuid().ToString(); | |
tracker[guid] = new Dump | |
{ | |
StackTrace = new StackTrace(true), | |
TimeStamp = DateTime.Now | |
}; | |
self = new ThreadLocal<object>(InitialValue); | |
} | |
public static void DumpTracked() | |
{ | |
var now = DateTime.Now; | |
using (var stream = new StreamWriter("dump_history.log", true)) | |
{ | |
stream.WriteLine("{0}, {1}", now.ToString("yyyy-MM-ddTHH:mm:ssZ"), tracker.Count); | |
} | |
using (var stream = new StreamWriter("dump.log")) | |
foreach (var dump in tracker.Where(x => (now - x.Value.TimeStamp) > TimeSpan.FromMinutes(5)).OrderBy(x => x.Value.TimeStamp)) | |
{ | |
stream.WriteLine("Age={0}, Creation-Time={1}, Id={2}", now - dump.Value.TimeStamp, dump.Value.TimeStamp, dump.Key); | |
stream.WriteLine(dump.Value.StackTrace); | |
stream.WriteLine(); | |
stream.WriteLine(); | |
} | |
} | |
public virtual System.Object InitialValue() | |
{ | |
return null; | |
} | |
public virtual System.Object Get() | |
{ | |
return self.Value; | |
} | |
public virtual void Set(System.Object object_Renamed) | |
{ | |
self.Value = object_Renamed; | |
} | |
public virtual void Close() | |
{ | |
Dump dump; | |
tracker.TryRemove(guid, out dump); | |
self.Value = null; | |
self.Dispose(); | |
} | |
public class Dump | |
{ | |
public StackTrace StackTrace { get; set; } | |
public DateTime TimeStamp { get; set; } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment