title: Use of ConcurrentHashMap and AtomicLong appear to be causing memory leak but not sure why
body: <p><strong>Issue</strong>: My code has a memory leak, and I don't see it. </p>
  
  <p><strong>Desired Outcome</strong>: I was hoping that someone would look over my code and offer suggestions as to where the memory leak could be.</p>
  
  <p><strong>Extra Credit</strong>: If you can think of a better way to get a thread dump for deadlock detection (given the restrictions stated below), I would be very interested in hearing about it :)</p>
  
  <p><strong>Background Information</strong>: I have a JVM instance were I have been trying to diagnose some thread deadlock. Due to lack of access to the instance, I could not do so with JConsole or VisualVM, so I was left with trying to write a thread dump to the logs when the deadlock occurs.</p>
  
  <p>I opted for using a <code>ConcurrentHashMap&lt;String, Long&gt;</code> where the <code>String</code> was the thread name, and the <code>Long</code> was the timestamp of the last know activity. The application is a Spring Boot app, and the map is inside a configuration <code>Bean</code> that is created on startup.</p>
  
  <p>When I run the code locally, I see that the names of the 13 threads I am interested in get added to the map (really I am only interested in 1 of the 13, but they are all created by a dependency that the application has, and are functionally related). When I run locally, I never see the map's size grow larger than 13 entries, and I have no reason to think that that would be the case in the target JVM instance. Still, after moving the code into the JVM instance, it was noticed that the memory kept slowly growing. I have since removed the code, but I don't see where my memory leak is.</p>
  
  <p>The <code>updateObjectLiveliness</code> method below is set up in such a way that all 13 threads call it once every second. So, as long as at least one of them is alive, the method will be run. Then, every 5 minutes, the map is checked to see if anything has not updated its timestamp in the last 5 minutes, and if so, run the thread dump code.</p>
  
  <p>Here is the class:</p>
  
  <pre><code>class ObjectLivelinessMonitor {
  
      private static final long FIVE_MINS_IN_MILLIS = 300000;
  
      private final ConcurrentHashMap&lt;String, Long&gt; nameOfThreadAndTimeStampOfLastActivity;
      private final Consumer&lt;Map.Entry&lt;String, Long&gt;&gt; handlerWhenUnlivelinessDetected;
      private final AtomicLong timeStamp;
  
      ObjectLivelinessMonitor(final Consumer&lt;Map.Entry&lt;String, Long&gt;&gt; handlerWhenUnlivelinessDetected) {
          this.nameOfThreadAndTimeStampOfLastActivity = new ConcurrentHashMap&lt;&gt;();
          this.handlerWhenUnlivelinessDetected = handlerWhenUnlivelinessDetected;
          this.timeStamp = new AtomicLong(System.currentTimeMillis());
      }
  
      void updateObjectLiveliness(final String nameOfThread) {
          final long currentTime = System.currentTimeMillis();
          nameOfThreadAndTimeStampOfLastActivity.put(nameOfThread, currentTime);
          if((timeStamp.get() + FIVE_MINS_IN_MILLIS) &lt; currentTime) {
              timeStamp.set(currentTime);
              for(final Map.Entry&lt;String, Long&gt; entry : nameOfThreadAndTimeStampOfLastActivity.entrySet()) {
                  final long howLongAgoWasLastKnownActivity = currentTime - entry.getValue();
                  if(howLongAgoWasLastKnownActivity &gt; FIVE_MINS_IN_MILLIS) {
                      this.handlerWhenUnlivelinessDetected.accept(entry);
                  }
              }
          }
      }
  }
  </code></pre>
  
  <p>And here is the thread dump code:</p>
  
  <pre><code>final Consumer&lt;Map.Entry&lt;String, Long&gt;&gt; handlerWhenUnlivelinessDetected = 
      (final Map.Entry&lt;String, Long&gt; entry) -&gt; {
          final String threadDump = Arrays.stream(ManagementFactory.getThreadMXBean()
                  .dumpAllThreads(true, true))
                  .map(ThreadInfo::toString)
                  .collect(Collectors.joining("\n"));
          logger.error(entry.getKey() + " has been inactive for " + entry.getValue() + " millis: " + threadDump);
  };
  </code></pre>
  
  <p>I am assuming that my error is somewhere in my use of either the <code>ConcurrentHashMap</code> or the <code>AtomicLong</code>, but after reading thru documentation online, and looking thru the source code for these two classes, I don't see what I am doing wrong.</p>