serializer icon indicating copy to clipboard operation
serializer copied to clipboard

LazyReferenceManager 100% cpu usage while idle

Open ZockiRR opened this issue 1 year ago • 2 comments

Environment Details

  • Eclipse Serializer Version: 1.3.1
  • JDK version: 21.0.2
  • OS: alpine linux or Win 10
  • Used frameworks: -

Describe the bug

After 1 hour of idle the LazyReferenceManager goes to 100% cpu usage. It directly goes back to normal when something gets loaded from the store.

To Reproduce

Use a LazyHashMap as a store for example. Store something in the LazyHashMap, use something from the LazyHashMap and than leave the app idle for 1 hour.

Expected behavior

LazyReferences get freed and the application stays at low cpu usage when idling.

htop from process

running in an alpine temurin pterodactly egg, but this also happens in my IDE on Win 10.

1147628 pteroda+  20   0   31.7g 316940  21956 R  99.9   0.2   1366:51 LazyReferenceMa                                                                                                                                                                                                  1147563 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:00.00 java
1147564 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:01.51 java
1147565 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:00.07 GC Thread#0
1147566 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:00.00 G1 Main Marker
1147567 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:00.02 G1 Conc#0
1147568 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:19.74 G1 Refine#0
1147569 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:03.10 G1 Service
1147570 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:58.98 VM Periodic Tas
1147571 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:03.06 VM Thread
1147572 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:00.00 Reference Handl
1147573 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:00.00 Finalizer
1147574 pteroda+  20   0   31.7g 316940  21956 S   0.0   0.2   0:00.00 Signal Dispatch

ZockiRR avatar Mar 28 '24 19:03 ZockiRR

I have tried to simulate this issue on more computers and more OS and I was not able to simulate this issue. Please provide us some repository with a minimal code to reproduce this issue.

image

zdenek-jonas avatar Apr 15 '24 09:04 zdenek-jonas

windows 10 image

zdenek-jonas avatar Apr 15 '24 09:04 zdenek-jonas

No activity since April, closing.

zdenek-jonas avatar Jul 09 '24 11:07 zdenek-jonas

I just wanted to add that I still have not been able to separate the issue with the time I have. If I find out where the problem is coming from, I will get back to you and reopen the issue if necessary. Nevertheless, thank you very much for your efforts!

ZockiRR avatar Oct 06 '24 11:10 ZockiRR

I was able to debug the issue and found that it comes from:

private void org.eclipse.serializer.reference.LazyReferenceManager.Default.updateStatistics() {
  /*
   * This method is intentionally not synchronized.
   * locking the LazyReferenceManager instance here may
   * cause deadlocks. See comments in internalCleanUp above.
   */
  
  int lazyReferences       = 0;
  int loadedLazyReferences = 0;
  
  for(Entry e = this.head; (e = e.nextLazyManagerEntry) != null;)
  {
    lazyReferences++;
    if(e.get().isLoaded()) // <---------------------------------------------------  e.get() is null
    {
      loadedLazyReferences++;
    }
  }
  
  this.monitor.update(lazyReferences, loadedLazyReferences);
}

Resulting in a catch here:

public void org.eclipse.serializer.reference.LazyReferenceManager.Default.LazyReferenceCleanupThread.run() {
  logger.debug("LazyReferenceManager started");
  
  LazyReferenceManager.Default parent;
  while((parent = this.parent.get()) != null) {
    // sleep for a dynamically specified milli time until the next check
    try {
      // check for running state. Must be the first action in case of swallowed exception
      if(!parent.isRunning()) {
        break;
      }
      
      // perform check
      parent.cleanUpBudgeted();
      
      // very nasty: must clear the reference from the stack in order for the WeakReference to work
      parent = null;
      
      // extra nasty: must sleep with nulled reference for WeakReference to work, not before.
      try {
        Thread.sleep(this.checkIntervalProvider.get()); // <------------------------ Resulting in skipping this sleep
      } catch(final InterruptedException e) {
        // sleep interrupted, proceed with check immediately
      }
    } catch(final Exception e) { // <----------------------------------------------- Exception caught here
      /*
       * Thread may not die on any exception, just continue looping
       * as long as parent exists and running is true
       */
    }
  }
  
  // either parent has been garbage collected or stopped, so terminate.
  logger.debug("LazyReferenceManager stopped");
}

Unfortunately, I am still not able to build a minimal example and I don't know how this condition arises. But maybe you can already do more with it.

ZockiRR avatar Oct 13 '24 21:10 ZockiRR