UI Freezes in JMix IntelliJ IDEA Plugin

Hi JMix team,

We at JetBrains have been investigating a number of UI freezes and noticed that several of them are caused by the JMix plugin. These freezes are quite severe and impact overall IDE responsiveness.

In most cases, the root cause appears to be the use of non-cancellable ReadAction on background threads. This pattern can easily lead to long UI freezes waiting for a write lock. There’s a detailed explanation of the problem and its implications here:

Here you can also checkout our SKILL for Claude Code that helps in UI freeze analysis

In this thread, I’ll share specific examples along with explanations and the corresponding thread stacks.

If helpful, we can also provide raw freeze reports including full thread dumps and metadata.

Freeze Report 71872713

Metadata

Field Value
Report ID 71872713
Duration 31 seconds
Product IntelliJ IDEA 2025.3.1 (IU-253.29346.138)
OS Linux x86_64 6.14.0-37-generic
Java 21.0.9+10-b1163.86 (JetBrains)
Plugin JavaScript and TypeScript 253.29346.138
Report Date 2025-05-13
Channel release
Sampled Time 15300ms
GC Time 1537ms (5%)
CPU Load 14%
Attachments dump-3.txt

Messages

  • Freeze for 31 seconds
  • Sampled time: 15300ms, sampling rate: 100ms, GC time: 1537ms (5%), Class loading: 0%, CPU load: 14%

Analysis

Cause

The EDT is blocked waiting to acquire a write lock (to run PsiManagerImpl.dropPsiCaches triggered by
TypeScriptServiceRestarter.restartServices). The write lock is blocked by a background thread holding a *
non-cancellable read action*.

Threads Participating

EDT (---------- EDT:):

  • State: TIMED_WAITING — waiting inside SuvorovProgress.dispatchEventsUntilComputationCompletes
  • Attempting to acquire write lock via NestedLocksThreadingSupport.prepareWriteFromWriteIntentBlocking
    ApplicationImpl.runWriteAction
  • Triggered by: TypeScriptServiceRestarter.restartServicesPsiManagerImpl.dropPsiCaches

Blocking thread ("DefaultDispatcher-worker-3"):

  • Running inside FileEditorProviderManagerImpl to determine applicable file editor providers
  • Holds non-cancellable read lock via ReadAction.computeEntityPropertiesEditorProvider.isEntity
    EntityUtil.isEntityEntityUtil.hasAnnotation
  • Inside the read action, uses Kotlin Analysis API (SymbolLightModifierList.getAnnotations) which triggers FIR lazy
    resolution → KotlinPackageIndexUtils.packageExists → index access → JS stub index update
  • During JS file indexing, builds PSI stubs (JSFileStubBuilder, JSVariableStubFactory) — this is the hot frame

Root Cause Frame

com.haulmont.jmixstudio.intellij.ui.entity.EntityPropertiesEditorProvider.accept
  → EntityUtil.isEntity
  → EntityUtil.hasAnnotation (via ReadAction.compute — non-cancellable)
    → Kotlin Analysis API (FirAnnotationUtils → FIR lazy resolution)
      → KotlinPackageIndexUtils.packageExists (index query)
        → FileBasedIndexImpl.forceUpdate (triggers JS stub indexing)

Problematic Plugin

Jmix Studio (io.jmix.studio / com.haulmont.jmixstudio)

EntityPropertiesEditorProvider.accept uses ReadAction.compute (non-cancellable) to check if a file is a Jmix entity.
This invokes Kotlin Analysis API which triggers FIR resolution and index queries, causing a long-running non-cancellable
read action that prevents write actions from proceeding.

Fix

Replace ReadAction.compute in EntityPropertiesEditorProvider.accept with a cancellable alternative (
ReadAction.nonBlocking().executeSynchronously() or smartReadAction coroutine API), or avoid triggering full Kotlin
FIR analysis / index updates from within a file editor provider check.

Freeze Report 74622093

Metadata

Field Value
Report ID 74622093
Duration 27 seconds
Product IntelliJ IDEA 2025.3.1 (IU-253.29346.138)
OS Windows 11 x86_64
Java 21.0.9+1-b1163.86 (JetBrains)
Report Date 2025-06-15
Channel release
Sampled Time 3200ms
GC Time 1467ms (6%)
CPU Load 65%
Attachments dump-1.txt

Messages

  • Freeze in EDT for 27 seconds
  • Sampled time: 3200ms, sampling rate: 100ms, GC time: 1467ms (6%), Class loading: 0%, CPU load: 65%

Analysis

Cause

The EDT is blocked waiting to acquire a write lock (to run ModuleRootModificationUtil.modifyModel). The write lock
is blocked by a background thread holding a non-cancellable read action via DumbService.runReadActionInSmartMode.

Threads Participating

EDT (---------- EDT:):

  • State: TIMED_WAITING — waiting in SuvorovProgress.dispatchEventsUntilComputationCompletes
  • Trying to acquire write lock via NestedLocksThreadingSupport.prepareWriteFromWriteIntentBlocking
    ApplicationImpl.runWriteActionWriteAction.run
  • Triggered by: ModuleRootModificationUtil.modifyModel

Blocking thread ("ApplicationImpl pooled thread 5"):

  • Running ComponentLibrary.scheduleInitComponentLibrary.initComponentLibrary.findGuiComponents
  • Holds non-cancellable read action via DumbService.runReadActionInSmartMode
  • Inside the read action, searches for inheritors of GUI components via JavaClassInheritorsSearcher
  • JavaDirectInheritorsSearcher.calculateDirectSubClasses uses a nested ReadAction.compute
  • Within that: PsiSearchHelperImpl.getUseScopeCubaScopeEnlarger.getAdditionalUseScope
    CubaWindows.isScreenControllerInheritanceUtil.isInheritor → class lookup → Kotlin stub index access →
    RegisteredIndexes.waitUntilIndicesAreInitialized (waiting for index init)

Root Cause Frame

ComponentLibrary.findGuiComponents (DumbService.runReadActionInSmartMode — non-cancellable)
  → JavaClassInheritorsSearcher / JavaDirectInheritorsSearcher.calculateDirectSubClasses
    → CubaScopeEnlarger.getAdditionalUseScope (ReadAction.compute — nested non-cancellable)
      → CubaWindows.isScreenController → InheritanceUtil.isInheritor
        → IdeKotlinDeclarationProvider.getAllClassesByClassId
          → FileBasedIndexImpl.ensureUpToDate
            → RegisteredIndexes.waitUntilIndicesAreInitialized  ← blocked here

Problematic Plugin

Jmix Studio (io.jmix.studio / com.haulmont.jmixstudio)

ComponentLibrary.scheduleInit initializes the GUI component library under a non-cancellable
DumbService.runReadActionInSmartMode. During search, CubaScopeEnlarger performs class hierarchy checks requiring
stub index access, which in turn waits for index initialization — all holding the read lock.

Fix

ComponentLibrary.scheduleInit should not use DumbService.runReadActionInSmartMode; use cancellable read actions (
ReadAction.nonBlocking()) or ensure index-dependent work runs outside a read action context.

Freeze Report 77908092

Metadata

Field Value
Report ID 77908092
Duration 44 seconds
Product IntelliJ IDEA 2025.3.1 (IU-253.29346.138)
OS Windows 10 x86_64
Java 21.0.9+1-b1163.86 (JetBrains)
Last Action Run
Report Date 2025-06-02
Channel release
Sampled Time 6800ms
GC Time 1184ms (3%)
CPU Load 12%
Attachments dump-4.txt

Messages

  • Freeze in EDT for 44 seconds
  • Sampled time: 6800ms, sampling rate: 100ms, GC time: 1184ms (3%), Class loading: 0%, CPU load: 12%

Analysis

Cause

The EDT is blocked waiting to acquire a write lock (to fire VirtualFileManagerImpl.notifyPropertyChanged). The
write lock is blocked by a background thread holding a non-cancellable read action via ActionsKt.runReadAction.

Threads Participating

EDT (---------- EDT:):

  • State: TIMED_WAITING — waiting in SuvorovProgress.dispatchEventsUntilComputationCompletes
  • Trying to acquire write lock via NestedLocksThreadingSupport.prepareWriteFromWriteIntentBlocking
    ApplicationImpl.runWriteAction
  • Triggered by: VirtualFileManagerImpl.notifyPropertyChanged

Blocking thread ("ApplicationImpl pooled thread 293"):

  • StatManager.collectAndSendStatsStatManager.fillOpenEventFlowViewLoader.loadScreens
  • FlowView.fromController runs ActionsKt.runReadAction (non-cancellable)
  • Inside the RA: FlowViewItem.fromFileFlowUtils.isViewOrFragmentDescriptorXmlHelper.getNamespace → XML PSI
    access → PsiFileImpl.loadTreeElementFileDocumentManagerBase.getDocumentLoadTextUtil.loadText
    EncodingManager.getInstance
  • Blocked at InstanceContainerImpl.getInstanceHolder — waiting for EncodingManager service initialization

Root Cause Frame

StatManager.collectAndSendStats → StatManager.fillOpenEvent
  → FlowViewLoader.loadScreens
    → FlowView.fromController (ActionsKt.runReadAction — non-cancellable)
      → FlowViewItem.fromFile → XmlHelper.getNamespace
        → XmlFileImpl.getDocument → PsiFileImpl.loadTreeElement
          → LoadTextUtil.loadText → EncodingManager.getInstance
            → InstanceContainerImpl.getInstanceHolder  ← blocked (service init)

Problematic Plugin

Jmix Studio (io.jmix.studio / com.haulmont.jmixstudio)

StatManager.collectAndSendStats runs FlowViewLoader.loadScreens under a non-cancellable runReadAction. During XML
file processing, it needs EncodingManager which may not be initialized yet, causing the service init to block inside
the read action, preventing the EDT’s write action from proceeding.

Fix

  • Move stat collection (StatManager.collectAndSendStats) out of a read action, or make it cancellable

Freeze Report 82205980

Metadata

Field Value
Report ID 82205980
Duration 17 seconds
Product IntelliJ IDEA 2025.3.2 (IU-253.30387.90)
OS Windows 11 x86_64
Java 21.0.9+1-b1163.94 (JetBrains)
Report Date 2026-03-18
Channel release
Sampled Time 2200ms
GC Time 356ms (2%)
CPU Load 17%
Attachments dump-1.txt

Messages

  • Freeze in EDT for 17 seconds
  • Sampled time: 2200ms, sampling rate: 100ms, GC time: 356ms (2%), Class loading: 0%, CPU load: 17%

Analysis

Cause

The EDT is blocked waiting to acquire a write lock (to run CoroutinesKt.edtWriteAction). The write lock is blocked
by a background thread holding a non-cancellable read action via ActionsKt.runReadAction.

Threads Participating

EDT (---------- EDT:):

  • State: TIMED_WAITING — waiting in SuvorovProgress.dispatchEventsUntilComputationCompletes
  • Trying to acquire write lock via NestedLocksThreadingSupport.prepareWriteFromWriteIntentBlocking
    ApplicationImpl.runWriteAction
  • Triggered by: CoroutinesKt.edtWriteAction

Blocking thread ("ApplicationImpl pooled thread 5"):

  • Running JmixProjectStartupActivity.markJmixSystemDirsAsExcludedJmixPropertiesManagerImpl.loadPropertySources
    JmixModuleTopologicalSort.doSort (recursive, 11 duplicate frames) → JmixModuleImpl.getDependencies
  • Holds non-cancellable read action via ActionsKt.runReadAction (called from JmixModuleImpl.getDependencies)
  • Inside the read action: compiled class reference resolution → ClsJavaCodeReferenceElementImpl.advancedResolve
    JavaPsiFacadeImpl.findClassScalaClassFinder.findClassScalaNamesUtil.cleanFqn → Scala collections
    mkString

Root Cause Frame

JmixProjectStartupActivity.markJmixSystemDirsAsExcluded
  → JmixPropertiesManagerImpl.loadPropertySources
    → JmixModuleTopologicalSort.doSort (recursive)
      → JmixModuleImpl.getDependencies (ActionsKt.runReadAction — non-cancellable)
        → ClsJavaCodeReferenceElementImpl.advancedResolve
          → JavaPsiFacadeImpl.findClass
            → ScalaClassFinder.findClass → ScalaNamesUtil.cleanFqn  ← hot frame

Problematic Plugin

Jmix Studio (io.jmix.studio / com.haulmont.jmixstudio)

JmixProjectStartupActivity.markJmixSystemDirsAsExcluded calls JmixModuleImpl.getDependencies inside a
non-cancellable runReadAction. The topological sort is recursive and each level acquires a new read action. Inside the
read action, class reference resolution triggers the Scala plugin’s class lookup, which runs through Scala collections
string operations — all while holding the non-cancellable read lock that prevents the EDT write action.

Fix

  • Make the read action in JmixModuleImpl.getDependencies cancellable (ReadAction.nonBlocking() or coroutine APIs)
  • Avoid recursive non-cancellable read actions in JmixModuleTopologicalSort.doSort

Freeze Report 86956299

Metadata

Field Value
Report ID 86956299
Duration 13 seconds
Product IntelliJ IDEA 2026.1 (IU-261.22158.277)
OS Windows 11 x86_64
Java 25.0.2+1-b329.72 (JetBrains)
Last Action ActivateProjectToolWindow
Report Date 2026-04-05
Channel release
Sampled Time 7100ms
GC Time 306ms (3%)
CPU Load 47%
Attachments dump-1.txt

Messages

  • Freeze in EDT for 13 seconds
  • Sampled time: 7100ms, sampling rate: 100ms, GC time: 306ms (3%), Class loading: 0%, CPU load: 47%

Analysis

Cause

The EDT is blocked waiting to acquire a write-intent lock (acquireWriteIntentPermit). The write-intent lock is
blocked by multiple background threads holding non-cancellable read actions via ApplicationImpl.runReadAction.

Threads Participating

EDT (---------- EDT:):

  • State: TIMED_WAITING — waiting in SuvorovProgress.dispatchEventsUntilComputationCompletes
  • Trying to acquire write-intent lock via NestedLocksThreadingSupport$ComputationState.acquireWriteIntentPermit
  • Triggered by: IdeEventQueue.dispatchByCustomDispatchers

Blocking thread ("DefaultDispatcher-worker-47"):

  • Running obfuscated K.mK.BT.B via EntityUtil.ra (outer ApplicationImpl.runReadAction — non-cancellable)
  • Inner ReadAction.compute via AnnotatedElementsSearcher.getAnnotationCandidates
    JavaAnnotationIndex.getAnnotations
  • Index access triggers FileBasedIndexImpl.forceUpdate → file type detection via VirtualFile.getFileType
  • FileTypeManagerImpl.getFileTypeByFileNameWildcardFileNameMatcher.acceptsCharSequence
    java.util.regex.Matcher.matches ← hot frame (non-cancellable regex)

RA0 ("DefaultDispatcher-worker-66"):

  • Same pattern: EntityUtil.raAnnotatedElementsSearcher → index update → VirtualFile.getFileType → regex
    matching
  • Outer context: StudioInfoProviderT.B

Root Cause Frame

EntityUtil.ra (ApplicationImpl.runReadAction — non-cancellable)
  → AnnotatedElementsSearcher.getAnnotationCandidates (ReadAction.compute — inner non-cancellable)
    → JavaAnnotationIndex.getAnnotations → StubIndexEx.getContainingIds
      → FileBasedIndexImpl.ensureUpToDate → FileBasedIndexImpl.forceUpdate
        → VirtualFile.getFileType → FileTypeManagerImpl.getFileTypeByFileName
          → WildcardFileNameMatcher → java.util.regex.Matcher.matches  ← blocked here

Problematic Plugin

Jmix Studio (io.jmix.studio / com.haulmont.jmixstudio)

EntityUtil.ra wraps entity annotation search in a non-cancellable ApplicationImpl.runReadAction. Inside,
AnnotatedElementsSearcher triggers index updates that perform file type detection using wildcard regex matching — a
non-cancellable CPU-bound operation. Multiple coroutine workers run this concurrently, saturating the thread pool and
starving the EDT of the write-intent lock.

Fix

  • Replace non-cancellable runReadAction in EntityUtil.ra with a cancellable alternative (ReadAction.nonBlocking()
    or coroutine readAction {})

Freeze Report 87427258

Metadata

Field Value
Report ID 87427258
Duration 30 seconds
Product IntelliJ IDEA 2026.1 (IU-261.22158.277)
OS Windows 11 x86_64
Java 25.0.2+1-b329.72 (JetBrains)
Report Date 2026-04-09
Channel release
Sampled Time 23900ms
GC Time 175ms (0%)
CPU Load 20%
Attachments dump-2.txt

Messages

  • Freeze for 30 seconds
  • Sampled time: 23900ms, sampling rate: 100ms, GC time: 175ms (0%), Class loading: 0%, CPU load: 20%
  • The stack is from the thread that was blocking EDT

Analysis

Cause

The EDT is BLOCKED (Java monitor) on a VFS directory data lock (VfsData$DirectoryData) owned by a background
thread. The background thread holds the VFS directory lock while waiting for disk IO (
DiskQueryRelay.accessDiskWithCheckCanceledArchiveFileSystem.getAttributes). Both threads are executing
HProjectUtils.isClassAvailableInLibrariesVirtualDirectoryImpl.findChild.

Threads Participating

EDT (---------- EDT:):

  • State: BLOCKED — waiting on com.intellij.openapi.vfs.newvfs.impl.VfsData$DirectoryData@2a0f182a owned by
    "DefaultDispatcher-worker-8"
  • Executing: JmixRunManagerListener.runConfigurationAddedJmixUtils.isJmixProject
    HProjectUtils.isClassAvailableInLibrariesReadAction.computeVirtualDirectoryImpl.findChild
    VirtualDirectoryImpl.findInPersistence ← BLOCKED here
  • Context: invoked via ActionsKt.invokeLater on the EDT inside TransactionGuardImpl.runWithWritingAllowed

Blocking thread ("DefaultDispatcher-worker-8"):

  • Same call path: JmixAiToolWindowFactory.shouldBeAvailableJmixUtils.checkJmixProject
    HProjectUtils.isClassAvailableInLibrariesReadAction.computeVirtualDirectoryImpl.findChild
    VirtualDirectoryImpl.findInPersistence
  • Holds the VFS DirectoryData lock while awaiting: PersistentFSImpl.findChildInfo
    ArchiveFileSystem.getAttributesDiskQueryRelay.accessDiskWithCheckCanceledFutureTask.get (waiting for disk
    IO)

Root Cause Frame

HProjectUtils.isClassAvailableInLibraries (ReadAction.compute — non-cancellable)
  → LibraryUtil.isClassAvailableInLibrary → LibraryUtil.findInFile
    → VirtualDirectoryImpl.findChild → VirtualDirectoryImpl.findInPersistence  ← EDT BLOCKED here
      (background thread holds DirectoryData lock)

Background thread holds DirectoryData lock while:
  PersistentFSImpl.findChildInfo → ArchiveFileSystem.getAttributes
    → DiskQueryRelay.accessDiskWithCheckCanceled → FutureTask.get  ← waiting for disk IO

Problematic Plugin

Jmix Studio (io.jmix.studio / com.haulmont.jmixstudio)

HProjectUtils.isClassAvailableInLibraries is called both from JmixAiToolWindowFactory.shouldBeAvailable (background
coroutine at project open) and from JmixRunManagerListener.runConfigurationAdded (EDT via invokeLater). Both calls
use ReadAction.compute and access VirtualDirectoryImpl.findChild. When the background thread holds the VFS
DirectoryData lock while waiting for archive attribute IO, the EDT’s own findChild call blocks on the same Java
monitor — causing the freeze.

The presence of DiskQueryRelay in the background thread indicates the IO is already properly delegated, but the VFS
directory lock is still held during the async wait, which causes the EDT contention.

Fix

  • Do not call HProjectUtils.isClassAvailableInLibraries (or any VFS access) from the EDT; defer to a background thread
    via ReadAction.nonBlocking()
  • JmixRunManagerListener.runConfigurationAdded should not invoke Jmix project checks synchronously on the EDT; use
    AppUIExecutor.onUiThread().inSmartMode() or a coroutine-based approach
  • The DiskQueryRelay approach in ArchiveFileSystem is appropriate for background threads, but the outer VFS
    directory lock must not be held during the IO wait — this requires fixing VFS internals or avoiding archive-backed
    library lookups in findChild paths called from EDT