Skip to content

Instantly share code, notes, and snippets.

Created August 18, 2011 15:52
Show Gist options
  • Save anonymous/1154378 to your computer and use it in GitHub Desktop.
Save anonymous/1154378 to your computer and use it in GitHub Desktop.
CloseableThreadLocal
/*
* 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