~
diff --git a/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt
index 1f77331..15a2c54 100644
--- a/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt
+++ b/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt
@@ -46,9 +46,7 @@
 import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
 import org.jetbrains.kotlin.backend.jvm.extensions.ClassGeneratorExtension
-import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
-import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
-import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
+import org.jetbrains.kotlin.cli.common.*
 import org.jetbrains.kotlin.cli.common.config.ContentRoot
 import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
 import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
@@ -57,8 +55,7 @@
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
 import org.jetbrains.kotlin.cli.common.messages.MessageCollector
-import org.jetbrains.kotlin.cli.common.perfManager
-import org.jetbrains.kotlin.cli.common.toBooleanLenient
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.Companion.resetApplicationManager
 import org.jetbrains.kotlin.cli.jvm.config.*
 import org.jetbrains.kotlin.cli.jvm.index.*
 import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
@@ -79,10 +76,7 @@
 import org.jetbrains.kotlin.idea.KotlinFileType
 import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory
 import org.jetbrains.kotlin.load.java.structure.impl.source.JavaFixedElementSourceFactory
-import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
-import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory
-import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
-import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
+import org.jetbrains.kotlin.load.kotlin.*
 import org.jetbrains.kotlin.parsing.KotlinParserDefinition
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
@@ -101,6 +95,7 @@
 import org.jetbrains.kotlin.utils.PathUtil
 import java.io.File
 import java.nio.file.FileSystems
+import java.util.concurrent.atomic.AtomicInteger
 import java.util.zip.ZipFile
 
 class KotlinCoreEnvironment private constructor(
@@ -571,8 +566,11 @@
                             ourApplicationEnvironment = null
                         }
                     })
+                } else {
+                    ApplicationManager.setApplication(ourApplicationEnvironment!!.application)
                 }
                 try {
+                    CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
                     val disposeAppEnv =
                         CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value.toBooleanLenient() != true
                     // Disposer uses identity of passed object to deduplicate registered disposables
@@ -591,6 +589,7 @@
                                     if (disposeAppEnv) {
                                         disposeApplicationEnvironment()
                                     } else {
+                                        MY_PERF_LOGGER.warn("Idle cleanup for module ${configuration.moduleName} #${cleanupCount.incrementAndGet()}")
                                         ourApplicationEnvironment?.idleCleanup()
                                     }
                                 }
@@ -861,3 +860,5 @@
         }
     }
 }
+
+private val cleanupCount = AtomicInteger(0)
\ No newline at end of file
diff --git a/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarVirtualFile.kt b/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarVirtualFile.kt
index 25977f4..26d41fb 100644
--- a/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarVirtualFile.kt
+++ b/compiler/cli/cli-base/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarVirtualFile.kt
@@ -24,6 +24,9 @@
     private val myChildrenList: MutableList<VirtualFile> = mutableListOf()
 
     init {
+        if (name.contains("ExceptionsKt.class")) {
+            Unit
+        }
         parent?.myChildrenList?.add(this)
     }
 
diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt
index 3f3b928..fde569b 100644
--- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt
+++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt
@@ -300,7 +300,13 @@
         valueDescription = "<path>",
         description = "Dump detailed performance statistics to the specified file."
     )
-    var dumpPerf: String? = "F:\\JetBrains\\logs\\perf.log.json"
+    var dumpPerf: String? = run {
+        if (System.getProperty("os.name").contains("Mac")) {
+            "/Users/Ivan.Kochurkin/Documents/JetBrains/logs/perf.log.json"
+        } else {
+            "F:\\JetBrains\\logs\\perf.log.json"
+        }
+    }
         set(value) {
             checkFrozen()
             field = if (value.isNullOrEmpty()) null else value
diff --git a/compiler/fir/stats-analyser/src/main.kt b/compiler/fir/stats-analyser/src/main.kt
index 1e6d62e..d6a28a0 100644
--- a/compiler/fir/stats-analyser/src/main.kt
+++ b/compiler/fir/stats-analyser/src/main.kt
@@ -28,6 +28,9 @@
     println()
     println("* Platform: ${totalStats.platform}")
     println("* Has errors: ${totalStats.hasErrors}")
+    println("* Modules count: ${modulesStats.modulesStats.size}")
+    println("* Files count: ${totalStats.initStats!!.filesCount}")
+    println("* Lines count: ${totalStats.initStats!!.linesCount}")
     println()
 
     val totalTime = totalStats.totalTime()
@@ -45,6 +48,8 @@
     println("-".repeat(50))
     printTimeAndRatio("TOTAL", totalTime, totalTime)
     println()
+    println("Total time: ${TimeUnit.NANOSECONDS.toSeconds(totalTime.nano)} s")
+    println()
 
     val analysisStatsTime = totalStats.analysisStats!!.time
 
@@ -66,18 +71,18 @@
     ) { it.analysisStats!!.time.userNano }
 
     modulesStats.printModulesBy("Find Kotlin Class Bytes") {
-        it.findKotlinClassStats!!.bytesCount
+        it.findKotlinClassStats?.bytesCount ?: 0
     }
     modulesStats.printModulesBy("Find Java Class Bytes") {
         it.findJavaClassStats!!.bytesCount
     }
     modulesStats.printModulesBy("Find Kotlin ms", printer = {
-            stats, nanos -> "${TimeUnit.NANOSECONDS.toMillis(nanos!!)} ms}"
+            stats, nanos -> "${TimeUnit.NANOSECONDS.toMillis(nanos!!)} ms"
     }) {
-        it.findKotlinClassStats!!.time.userNano
+        it.findKotlinClassStats?.time?.userNano ?: 0
     }
     modulesStats.printModulesBy("Find Java ms", printer = {
-        stats, nanos -> "${TimeUnit.NANOSECONDS.toMillis(nanos!!)} ms}"
+        stats, nanos -> "${TimeUnit.NANOSECONDS.toMillis(nanos!!)} ms"
     }) {
         it.findJavaClassStats!!.time.userNano
     }
@@ -99,7 +104,6 @@
             it.totalTime().nano
         }
     }*/
-    println("Total time: ${totalTime.millis} ms")
     println("Total Java class bytes count: ${totalStats.findJavaClassStats!!.bytesCount} bytes")
     println("Total Kotlin class bytes count: ${totalStats.findKotlinClassStats!!.bytesCount} bytes")
 }
diff --git a/compiler/frontend.common.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt b/compiler/frontend.common.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt
index 21a36e0..e2165ed 100644
--- a/compiler/frontend.common.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt
+++ b/compiler/frontend.common.jvm/src/org/jetbrains/kotlin/load/kotlin/KotlinBinaryClassCache.kt
@@ -11,61 +11,25 @@
 import com.intellij.openapi.util.Computable
 import com.intellij.openapi.vfs.VirtualFile
 import com.intellij.psi.PsiJavaModule
+import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder.Result.KotlinClass
 import org.jetbrains.kotlin.metadata.deserialization.MetadataVersion
 import org.jetbrains.kotlin.util.PerformanceManager
+import org.jetbrains.kotlin.utils.PrintingLogger
 import java.io.File
-import java.lang.ref.WeakReference
-import java.util.concurrent.CopyOnWriteArrayList
+import java.io.PrintStream
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
 
 class KotlinBinaryClassCache : Disposable {
-    private val requestCaches = CopyOnWriteArrayList<WeakReference<RequestCache>>()
-
-    private class RequestCache {
-        var virtualFile: VirtualFile? = null
-        var modificationStamp: Long = 0
-        var result: KotlinClassFinder.Result? = null
-
-        fun cache(
-            file: VirtualFile,
-            result: KotlinClassFinder.Result?
-        ): KotlinClassFinder.Result? {
-            virtualFile = file
-            this.result = result
-            modificationStamp = file.modificationStamp
-
-            return result
-        }
-    }
-
-    private val cache = object : ThreadLocal<RequestCache>() {
-        override fun initialValue(): RequestCache {
-            return RequestCache().also {
-                requestCaches.add(WeakReference(it))
-            }
-        }
-    }
+    private val cache: ConcurrentHashMap<Pair<String, Long>, KotlinClassFinder.Result?> = ConcurrentHashMap()
 
     override fun dispose() {
-        for (cache in requestCaches) {
-            cache.get()?.run {
-                result = null
-                virtualFile = null
-            }
-        }
-        requestCaches.clear()
-        // This is only relevant for tests. We create a new instance of Application for each test, and so a new instance of this service is
-        // also created for each test. However all tests share the same event dispatch thread, which would collect all instances of this
-        // thread-local if they're not removed properly. Each instance would transitively retain VFS resulting in OutOfMemoryError
-        cache.remove()
-
-        synchronized(writeLock) {
-            File("F:\\JetBrains\\logs\\temp.txt").appendText("dispose\n")
-        }
+        cache.clear()
+        MY_PERF_LOGGER.info("KotlinBinaryClassCache is disposed")
     }
 
     companion object {
-        var count = 0
-        val writeLock = Any()
+        var count: AtomicInteger = AtomicInteger(0)
 
         @Deprecated(
             "Please pass metadataVersion explicitly",
@@ -95,28 +59,45 @@
 
             if (file.name == PsiJavaModule.MODULE_INFO_CLS_FILE) return null
 
+            val currentThreadId = Thread.currentThread().id
+
             val application = ApplicationManager.getApplication()
             val service = application.getService(KotlinBinaryClassCache::class.java)
-            val requestCache = service.cache.get()
+            val cache = service.cache
 
-            if (file.modificationStamp == requestCache.modificationStamp && file == requestCache.virtualFile) {
-                return requestCache.result
+            val isExceptionsClass = file.toString().contains("kotlin${File.separator}ExceptionsKt.class")
+            if (isExceptionsClass) {
+                MY_PERF_LOGGER.info("${file.url} is accessed, metadata: $metadataVersion current thread id is $currentThreadId;")
             }
 
-            if (file.toString().contains("kotlin\\ExceptionsKt.class")) {
-                synchronized(writeLock) {
-                    "Load ${file} ${count++} time".let {
-                        println(it)
-                        File("F:\\JetBrains\\logs\\temp.txt").appendText(it + "\n")
+            return cache.getOrPut(file.url to file.modificationStamp) {
+                if (isExceptionsClass) {
+                    MY_PERF_LOGGER.info("${file.url} is created, metadata: $metadataVersion, current thread id is $currentThreadId;")
+                }
+
+                application.runReadAction(Computable {
+                    VirtualFileKotlinClass.create(file, metadataVersion, fileContent, perfManager)
+                }).also {
+                    if (count.incrementAndGet() % 32 == 0) {
+                        val totalSizeInBytes = cache.values.fold(0L) { acc, result ->
+                            acc + when (result) {
+                                is KotlinClass -> result.byteContent?.size ?: 0
+                                is KotlinClassFinder.Result.ClassFileContent -> result.content.size
+                                null -> 0
+                            }
+                        }
+                        MY_PERF_LOGGER.info("Total size of KotlinBinaryClassCache is ${totalSizeInBytes / 1024} KB")
                     }
                 }
             }
-
-            val aClass = application.runReadAction(Computable {
-                VirtualFileKotlinClass.create(file, metadataVersion, fileContent, perfManager)
-            })
-
-            return requestCache.cache(file, aClass)
         }
     }
 }
+
+val MY_PERF_LOGGER = PrintingLogger(PrintStream(
+        if (System.getProperty("os.name").contains("Mac")) {
+            "/Users/Ivan.Kochurkin/Documents/JetBrains/logs/perf.info.log"
+        } else {
+            "F:\\JetBrains\\logs\\test.log"
+        }
+    ))
diff --git a/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt b/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt
index 3a66727..d54cfb9 100644
--- a/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt
+++ b/jps/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt
@@ -340,6 +340,8 @@
         if (System.getProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "false").toBoolean())
             CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
 
+        CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
+
         val rc = environment.withProgressReporter { progress ->
             progress.compilationStarted()
             CompilerRunnerUtil.invokeExecMethod(compilerClassName, withAdditionalCompilerArgs(compilerArgs), environment, out)