title: Whether ThreadLocal value is GCed in Java ThreadPool?
body: <p>In my code, I want to test ThreadLocal's GC strategy. I use two methods. One is <code>ThreadPool</code>, the other is a self-created thread. In the first scenarios, <code>JVM</code> doesn't GC Thread's <code>ThreadLocalMap</code> seemly(No <code>finalize()</code> output). The other works well.</p>
  
  <p>I've found. In October 2007, Josh Bloch (co-author of java.lang.ThreadLocal along with Doug Lea) wrote:</p>
  
  <blockquote>
    <p>"The use of thread pools demands extreme care. Sloppy use of thread
    pools in combination with sloppy use of thread locals can cause
    unintended object retention, as has been noted in many places."</p>
  </blockquote>
  
  <p>I guess <code>ThreadPool</code> may be dangerous to use <code>ThreadLocal</code>.</p>
  
  <p>Here is my code(JDK8 environment)</p>
  
  <pre><code>public class ThreadLocalDemo_Gc {
      static volatile ThreadLocal&lt;SimpleDateFormat&gt; tl = new ThreadLocal&lt;SimpleDateFormat&gt;(){
          // overwrite finalize, such that the message will be printed when GC happens.
          protected void finalize() throws Throwable{
              System.out.println(this.toString() + " is gc(threadlocal)");
          }
      };
  
      // Let the main thread wait for all workers.
      static volatile CountDownLatch cd = new CountDownLatch(10);
  
      public static class ParseDate implements Runnable{
          int i = 0;
          public ParseDate(int i) {
              super();
              this.i = i;
          }
  
          @Override
          public void run() {
              try {
                  if(tl.get() == null){
                      tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"){
                          // overwrite finalize, such that the message will be printed when GC happens.
                          protected void finalize() throws Throwable {
                              System.out.println(this.toString() + " is gc(sdf)");
                          }
                      });
                      // new sdf object is created in ThreadLocalMap
                      System.out.println(Thread.currentThread().getId() + ":create SimpleDateFormat");
                  }
  
                  Date t = tl.get().parse("2017-3-26 17:03:" + i % 60);
  
              } catch (ParseException e) {
                  e.printStackTrace();
              } finally {
                  cd.countDown();
              }
          }   
      }
  
      // code with ThreadPool
  //  public static void main(String[] args) throws InterruptedException {
  //      ExecutorService es = Executors.newFixedThreadPool(10);
  //      
  //      for(int i = 0; i &lt; 10; i++){
  //          es.execute(new ParseDate(i));
  //      }
  //      cd.await();
  //      
  //      System.out.println("mission complete");
  //      
  //      tl = null;  // free the weak reference
  //      System.gc();
  //      System.out.println("First GC complete");
  //      es.shutdown();
  //  }
  
      // not pooling threads
      public static void main(String[] args) throws InterruptedException {
          Thread[] all = new Thread[10];
  
          for(int i = 0; i &lt; 10; i++){
              all[i] = new Thread(new ParseDate(i));
          }
  
          for(int i =  0; i &lt; 10; i++){
              all[i].start();
          }
  
          cd.await();
  
          tl = null;
  
          System.gc();
          System.out.println("First GC complete");
      }
  
  }
  </code></pre>
  
  <p>After running the first <code>main()</code> function. None of the <code>SimpleDateFormat</code> object is GCed. The second <code>main()</code> function indeed does that job.</p>
  
  <p>first <code>main</code> function
  <a href="https://i.stack.imgur.com/qXE84.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/qXE84.png" alt="first main function"></a></p>
  
  <p>second <code>main</code> function
  <a href="https://i.stack.imgur.com/QyG5g.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/QyG5g.png" alt="second main function"></a></p>
  
  <p>Edit #1
  Thanks to Gray's remind. The real problem, which results in no output in the function <code>finalize()</code>, is the <code>ThreadPool</code> may not be truly collected. In the test code, only <code>shutdown()</code> was used. However, the worker threads may not be collected after this process. So more safer way is invoking <code>awaitTermination()</code>. This function does generate all worker threads instance, and the resource those belongs to is collected, spefically <code>ThreadLocalMap</code>.</p>
  
  <p>Here is the revision of the <code>main()</code> with <code>ThreadPool</code></p>
  
  <pre><code>// code with ThreadPool
      public static void main(String[] args) throws InterruptedException {
          ExecutorService es = Executors.newFixedThreadPool(10);
  
          for(int i = 0; i &lt; 10; i++){
              es.execute(new ParseDate(i));
          }
          cd.await();
  
          es.shutdown();
          es.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
          System.gc();
  
      }
  </code></pre>
  
  <p>This version of <code>main()</code> works well, all the collection message from <code>finalize()</code> method are printed. </p>
  
  <p>Finally, Java GC may not collect the <strong>value</strong> when the instance of <code>Entry</code>'s key has no stable reference. As <code>ThreadLocalMap</code>'s key is the weak reference, the <code>Entry</code>'s key becomes <code>null</code>. However, the <code>Entry</code>'s value is not GCed. This conclusion may be proved in my test.</p>