Fix potential memory leak in light-classes
The problem is that they might indirectly retain a reference to an obsolete ResolverForProject (it becomes outdated after out of block modification). The best solution would be to avoid preserving jvm-related diagnostics (that are only used once to report them when highlighting, and exactly they retains an obsolete resolve session) But it seems that this may lead to a deep light-classes refactoring and the temporary fix is storing diagnostics themselves in a CachedValue bound to out of block modification tracker. There're no guarantees that this would help, since CachedValue anyway is based on soft-reference that is not the first thing GC collects
This commit is contained in:
+8
-2
@@ -55,15 +55,19 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
|||||||
return if (classOrObject.isLocal) {
|
return if (classOrObject.isLocal) {
|
||||||
LazyLightClassDataHolder.ForClass(
|
LazyLightClassDataHolder.ForClass(
|
||||||
builder,
|
builder,
|
||||||
|
classOrObject.project,
|
||||||
exactContextProvider = { IDELightClassContexts.contextForLocalClassOrObject(classOrObject) },
|
exactContextProvider = { IDELightClassContexts.contextForLocalClassOrObject(classOrObject) },
|
||||||
dummyContextProvider = null
|
dummyContextProvider = null,
|
||||||
|
isLocal = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LazyLightClassDataHolder.ForClass(
|
LazyLightClassDataHolder.ForClass(
|
||||||
builder,
|
builder,
|
||||||
|
classOrObject.project,
|
||||||
exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) },
|
exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) },
|
||||||
dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) }
|
dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) },
|
||||||
|
isLocal = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,6 +80,7 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
|||||||
|
|
||||||
return LazyLightClassDataHolder.ForFacade(
|
return LazyLightClassDataHolder.ForFacade(
|
||||||
builder,
|
builder,
|
||||||
|
files.first().project,
|
||||||
exactContextProvider = { IDELightClassContexts.contextForFacade(sortedFiles) },
|
exactContextProvider = { IDELightClassContexts.contextForFacade(sortedFiles) },
|
||||||
dummyContextProvider = { IDELightClassContexts.lightContextForFacade(sortedFiles) }
|
dummyContextProvider = { IDELightClassContexts.lightContextForFacade(sortedFiles) }
|
||||||
)
|
)
|
||||||
@@ -84,6 +89,7 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
|||||||
override fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript {
|
override fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript {
|
||||||
return LazyLightClassDataHolder.ForScript(
|
return LazyLightClassDataHolder.ForScript(
|
||||||
builder,
|
builder,
|
||||||
|
script.project,
|
||||||
exactContextProvider = { IDELightClassContexts.contextForScript(script) },
|
exactContextProvider = { IDELightClassContexts.contextForScript(script) },
|
||||||
dummyContextProvider = { IDELightClassContexts.lightContextForScript(script) }
|
dummyContextProvider = { IDELightClassContexts.lightContextForScript(script) }
|
||||||
)
|
)
|
||||||
|
|||||||
+28
-13
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.caches.resolve.lightClasses
|
package org.jetbrains.kotlin.idea.caches.resolve.lightClasses
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
import com.intellij.psi.PsiMember
|
import com.intellij.psi.PsiMember
|
||||||
import com.intellij.psi.PsiMethod
|
import com.intellij.psi.PsiMethod
|
||||||
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
||||||
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
|
import com.intellij.psi.util.PsiModificationTracker
|
||||||
import org.jetbrains.kotlin.asJava.LightClassBuilder
|
import org.jetbrains.kotlin.asJava.LightClassBuilder
|
||||||
import org.jetbrains.kotlin.asJava.builder.*
|
import org.jetbrains.kotlin.asJava.builder.*
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
@@ -37,23 +41,31 @@ typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?
|
|||||||
|
|
||||||
sealed class LazyLightClassDataHolder(
|
sealed class LazyLightClassDataHolder(
|
||||||
builder: LightClassBuilder,
|
builder: LightClassBuilder,
|
||||||
|
project: Project,
|
||||||
exactContextProvider: ExactLightClassContextProvider,
|
exactContextProvider: ExactLightClassContextProvider,
|
||||||
dummyContextProvider: DummyLightClassContextProvider
|
dummyContextProvider: DummyLightClassContextProvider,
|
||||||
|
isLocal: Boolean = false
|
||||||
) : LightClassDataHolder {
|
) : LightClassDataHolder {
|
||||||
|
|
||||||
private val exactResultLazyValue = lazyPub { builder(exactContextProvider()) }
|
private val exactResultCachedValue =
|
||||||
|
CachedValuesManager.getManager(project).createCachedValue({
|
||||||
private val exactResult: LightClassBuilderResult by exactResultLazyValue
|
CachedValueProvider.Result.create(
|
||||||
|
builder(exactContextProvider()),
|
||||||
|
if (isLocal)
|
||||||
|
PsiModificationTracker.MODIFICATION_COUNT
|
||||||
|
else
|
||||||
|
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||||
|
}, false)
|
||||||
|
|
||||||
private val lazyInexactStub by lazyPub {
|
private val lazyInexactStub by lazyPub {
|
||||||
dummyContextProvider?.let { provider -> provider()?.let { context -> builder.invoke(context).stub } }
|
dummyContextProvider?.let { provider -> provider()?.let { context -> builder.invoke(context).stub } }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inexactStub: PsiJavaFileStub?
|
private val inexactStub: PsiJavaFileStub?
|
||||||
get() = if (exactResultLazyValue.isInitialized()) null else lazyInexactStub
|
get() = if (exactResultCachedValue.hasUpToDateValue()) null else lazyInexactStub
|
||||||
|
|
||||||
override val javaFileStub get() = exactResult.stub
|
override val javaFileStub get() = exactResultCachedValue.value.stub
|
||||||
override val extraDiagnostics get() = exactResult.diagnostics
|
override val extraDiagnostics get() = exactResultCachedValue.value.diagnostics
|
||||||
|
|
||||||
// for facade or defaultImpls
|
// for facade or defaultImpls
|
||||||
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData =
|
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData =
|
||||||
@@ -62,8 +74,9 @@ sealed class LazyLightClassDataHolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ForClass(
|
class ForClass(
|
||||||
builder: LightClassBuilder, exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
|
builder: LightClassBuilder, project: Project, isLocal: Boolean,
|
||||||
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForClass {
|
exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
|
||||||
|
) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider, isLocal), LightClassDataHolder.ForClass {
|
||||||
override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData =
|
override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData =
|
||||||
LazyLightClassData { stub ->
|
LazyLightClassData { stub ->
|
||||||
stub.findDelegate(classOrObject)
|
stub.findDelegate(classOrObject)
|
||||||
@@ -71,12 +84,14 @@ sealed class LazyLightClassDataHolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ForFacade(
|
class ForFacade(
|
||||||
builder: LightClassBuilder, exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
|
builder: LightClassBuilder, project: Project,
|
||||||
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForFacade
|
exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
|
||||||
|
) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForFacade
|
||||||
|
|
||||||
class ForScript(
|
class ForScript(
|
||||||
builder: LightClassBuilder, exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
|
builder: LightClassBuilder, project: Project,
|
||||||
) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForScript
|
exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
|
||||||
|
) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForScript
|
||||||
|
|
||||||
private inner class LazyLightClassData(
|
private inner class LazyLightClassData(
|
||||||
findDelegate: (PsiJavaFileStub) -> PsiClass
|
findDelegate: (PsiJavaFileStub) -> PsiClass
|
||||||
|
|||||||
Reference in New Issue
Block a user