Java 如何清理垃圾

Java 如何清理垃圾,java,tomcat,thread-local,Java,Tomcat,Thread Local,有没有人举过这样的例子?它们是由垃圾收集器处理的吗?我正在使用Tomcat 6。javadoc说: “只要线程处于活动状态且线程本地实例可访问,每个线程都持有对其线程本地变量副本的隐式引用;线程退出后,其线程本地实例的所有副本都将接受垃圾收集(除非存在对这些副本的其他引用) 如果您的应用程序或(如果您谈论的是请求线程)容器使用线程池,这意味着线程不会消亡。如果需要,您需要自己处理线程局部变量。唯一干净的方法是调用ThreadLocal.remove()方法 您可能希望清除线程池中线程的线程局部变

有没有人举过这样的例子?它们是由垃圾收集器处理的吗?我正在使用Tomcat 6。

javadoc说:

“只要线程处于活动状态且线程本地实例可访问,每个线程都持有对其线程本地变量副本的隐式引用;线程退出后,其线程本地实例的所有副本都将接受垃圾收集(除非存在对这些副本的其他引用)

如果您的应用程序或(如果您谈论的是请求线程)容器使用线程池,这意味着线程不会消亡。如果需要,您需要自己处理线程局部变量。唯一干净的方法是调用
ThreadLocal.remove()
方法

您可能希望清除线程池中线程的线程局部变量有两个原因:

  • 防止内存(或假设资源)泄漏,或
  • 防止信息通过线程局部变量从一个请求意外泄漏到另一个请求
线程本地内存泄漏通常不应该是有界线程池的主要问题,因为任何线程本地内存最终都可能被覆盖;即当线程被重用时。但是,如果您错误地一次又一次地创建新的
ThreadLocal
实例(而不是使用
静态
变量来保存单实例),线程本地值不会被覆盖,并且会在每个线程的
线程本地值
映射中累积。这可能会导致严重泄漏


假设您谈论的是在Web应用程序处理HTTP请求期间创建/使用的线程局部变量,那么避免线程局部泄漏的一种方法是使用您的Web应用程序的
ServletContext
注册
ServletRequestListener
,并实现侦听器的
requestdestromed
方法进行清理当前线程的线程局部变量


注意,在此上下文中,您还需要考虑从一个请求到另一个请求的信息泄漏的可能性。

< P>无法清除<代码>线程本地< /COD>值,除非从线程放在第一位的线程内(或者当线程是垃圾收集时-而不是用工作者线程的情况)。。这意味着您应该在servlet请求完成时(或在将AsyncContext传输到servlet 3中的另一个线程之前)注意清理ThreadLocal,因为在这一点之后,您可能再也没有机会进入特定的工作线程,因此,在服务器未重新启动的情况下,如果您的web应用程序未部署,将导致内存泄漏

进行此类清理的一个好地方是

如果您使用Spring,所有必要的连接都已经就绪,您可以简单地将内容放入请求范围,而不用担心清理它们(这会自动发生):


以下是一些代码,当您没有对实际线程局部变量的引用时,可以从当前线程中清除所有线程局部变量。您还可以将其概括为清除其他线程的线程局部变量:

    private void cleanThreadLocals() {
        try {
            // Get a reference to the thread locals table of the current thread
            Thread thread = Thread.currentThread();
            Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true);
            Object threadLocalTable = threadLocalsField.get(thread);

            // Get a reference to the array holding the thread local variables inside the
            // ThreadLocalMap of the current thread
            Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
            Field tableField = threadLocalMapClass.getDeclaredField("table");
            tableField.setAccessible(true);
            Object table = tableField.get(threadLocalTable);

            // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
            // is a reference to the actual ThreadLocal variable
            Field referentField = Reference.class.getDeclaredField("referent");
            referentField.setAccessible(true);

            for (int i=0; i < Array.getLength(table); i++) {
                // Each entry in the table array of ThreadLocalMap is an Entry object
                // representing the thread local reference and its value
                Object entry = Array.get(table, i);
                if (entry != null) {
                    // Get a reference to the thread local object and remove it from the table
                    ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
                    threadLocal.remove();
                }
            }
        } catch(Exception e) {
            // We will tolerate an exception here and just log it
            throw new IllegalStateException(e);
        }
    }
private void cleanThreadLocals(){
试一试{
//获取对当前线程的线程局部变量表的引用
Thread-Thread=Thread.currentThread();
字段threadLocalsField=Thread.class.getDeclaredField(“threadLocals”);
threadLocalsField.setAccessible(true);
对象threadLocalTable=threadLocalsField.get(线程);
//获取对数组的引用,该数组在
//当前线程的ThreadLocalMap
类threadLocalMapClass=Class.forName(“java.lang.ThreadLocal$ThreadLocalMap”);
Field tableField=threadLocalMapClass.getDeclaredField(“表格”);
tableField.setAccessible(true);
objecttable=tableField.get(threadLocalTable);
//ThreadLocalMap的键是WeakReference对象。此对象的referent字段
//是对实际ThreadLocal变量的引用
字段referentField=Reference.class.getDeclaredField(“referent”);
referencetfield.setAccessible(true);
for(int i=0;i
我想对这个问题给出我的答案,尽管这个问题已经很老了。我也被同样的问题困扰过(gson threadlocal没有从请求线程中删除),甚至在服务器内存耗尽时重新启动服务器(这太浪费时间了!!)

在设置为dev模式的javaweb应用程序的上下文中(服务器设置为在每次检测到代码更改时都会跳出,并且可能也在调试模式下运行),我很快了解到threadlocals可能非常棒,有时也会让人感到痛苦。我对每个请求都使用threadlocal调用。在调用内部。有时我还使用gson生成响应。我会将调用包装在过滤器中的“try”块中,并在“finally”块中销毁它

我所观察到的(我现在还没有数据支持)是,如果我对几个文件进行了更改,并且服务器在我的更改之间不断跳转,我会得到impati
    private void cleanThreadLocals() {
        try {
            // Get a reference to the thread locals table of the current thread
            Thread thread = Thread.currentThread();
            Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true);
            Object threadLocalTable = threadLocalsField.get(thread);

            // Get a reference to the array holding the thread local variables inside the
            // ThreadLocalMap of the current thread
            Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
            Field tableField = threadLocalMapClass.getDeclaredField("table");
            tableField.setAccessible(true);
            Object table = tableField.get(threadLocalTable);

            // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
            // is a reference to the actual ThreadLocal variable
            Field referentField = Reference.class.getDeclaredField("referent");
            referentField.setAccessible(true);

            for (int i=0; i < Array.getLength(table); i++) {
                // Each entry in the table array of ThreadLocalMap is an Entry object
                // representing the thread local reference and its value
                Object entry = Array.get(table, i);
                if (entry != null) {
                    // Get a reference to the thread local object and remove it from the table
                    ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
                    threadLocal.remove();
                }
            }
        } catch(Exception e) {
            // We will tolerate an exception here and just log it
            throw new IllegalStateException(e);
        }
    }
public class MyObjectHolder {

    private MyObject myObject;

    public MyObjectHolder(MyObject myObj) {
        myObject = myObj;
    }

    public MyObject getMyObject() {
        return myObject;
    }

    protected void finalize() throws Throwable {
        myObject.cleanItUp();
    }
}

public class SomeOtherClass {
    static ThreadLocal<MyObjectHolder> threadLocal = new ThreadLocal<MyObjectHolder>();
    .
    .
    .
}
private static final MethodHandle        s_getThreadLocals     = initThreadLocals();
private static final MethodHandle        s_expungeStaleEntries = initExpungeStaleEntries();
private static final ThreadLocal<Object> s_threadLocals        = ThreadLocal.withInitial(() -> getThreadLocals());

public static void expungeThreadLocalMap()
{
   Object threadLocals;

   threadLocals = s_threadLocals.get();

   try
   {
      s_expungeStaleEntries.invoke(threadLocals);
   }
   catch (Throwable e)
   {
      throw new IllegalStateException(e);
   }
}

private static Object getThreadLocals()
{
   ThreadLocal<Object> local;
   Object result;
   Thread thread;

   local = new ThreadLocal<>();

   local.set(local);   // Force ThreadLocal to initialize Thread.threadLocals

   thread = Thread.currentThread();

   try
   {
      result = s_getThreadLocals.invoke(thread);
   }
   catch (Throwable e)
   {
      throw new IllegalStateException(e);
   }

   return(result);
}

private static MethodHandle initThreadLocals()
{
   MethodHandle result;
   Field field;

   try
   {
      field = Thread.class.getDeclaredField("threadLocals");

      field.setAccessible(true);

      result = MethodHandles.
         lookup().
         unreflectGetter(field);

      result = Preconditions.verifyNotNull(result, "result is null");
   }
   catch (NoSuchFieldException | SecurityException | IllegalAccessException e)
   {
      throw new ExceptionInInitializerError(e);
   }

   return(result);
}

private static MethodHandle initExpungeStaleEntries()
{
   MethodHandle result;
   Class<?> clazz;
   Method method;
   Object threadLocals;

   threadLocals = getThreadLocals();
   clazz        = threadLocals.getClass();

   try
   {
      method = clazz.getDeclaredMethod("expungeStaleEntries");

      method.setAccessible(true);

      result = MethodHandles.
         lookup().
         unreflect(method);
   }
   catch (NoSuchMethodException | SecurityException | IllegalAccessException e)
   {
      throw new ExceptionInInitializerError(e);
   }

   return(result);
}