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:
Denis Zharkov
2017-11-15 11:24:08 +03:00
parent a67598e0c1
commit 2d82cb936e
2 changed files with 36 additions and 15 deletions
@@ -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) }
) )
@@ -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