提交 c3d31603 编写于 作者: I Ilya Chernikov

Cleaning jar cache after build in gradle plugin, fixes KT-9440. Some...

Cleaning jar cache after build in gradle plugin, fixes KT-9440. Some refactorings on the way to fix.
上级 ed5c059c
......@@ -16,13 +16,11 @@
package org.jetbrains.kotlin.gradle.plugin
import org.apache.commons.lang.SystemUtils
import org.gradle.BuildAdapter
import org.gradle.BuildResult
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging
import java.lang.ref.Reference
import java.util.concurrent.ScheduledExecutorService
import kotlin.text.MatchGroup
internal fun getUsedMemoryKb(): Long {
System.gc()
......@@ -42,7 +40,7 @@ private fun comparableVersionStr(version: String) =
?.joinToString(".", transform = { it!!.value.padStart(3, '0') })
class FinishBuildListener(pluginClassLoader: ClassLoader?, val startMemory: Long) : BuildAdapter() {
class FinishBuildListener(pluginClassLoader: ClassLoader, val startMemory: Long) : BuildAdapter() {
val log = Logging.getLogger(this.javaClass)
private var threadTracker: ThreadTracker? = ThreadTracker()
......@@ -52,26 +50,11 @@ class FinishBuildListener(pluginClassLoader: ClassLoader?, val startMemory: Long
override fun buildFinished(result: BuildResult?) {
log.kotlinDebug("Build finished listener")
val gradle = result?.getGradle()
val gradle = result?.gradle
if (gradle != null) {
// making cleanup only on recognized versions of gradle and if version < 2.4
// otherwise it may cause problems e.g. with JobScheduler on subsequent runs
// this strategy may lead to memory leaks, but prevent crashes due to destroyed JobScheduler
// the reason for the strategy is the following:
// gradle < 2.4 has problems with plugin reuse in the daemon: new calls to the plugin are made with a new classloader
// for every new build. With statically initialized daemons like JobScheduler that leads to big leaks of classloaders and classes,
// therefore to reduce leaks JobScheduler (and deprecated ZipFileCache for now) should be stopped)
// It should be noted that because of this behavior there are no benefits of using daemon in these versions.
// Starting from 2.4 gradle using cached classloaders, that leads to effective class reusing in the daemon, but
// in that case premature stopping of the static daemons may lead to crashes.
comparableVersionStr(gradle.getGradleVersion())?.let {
log.kotlinDebug("detected gradle version $it")
if (it < comparableVersionStr("2.4")!!) {
cleanup()
// checking thread leaks only then cleaning up
threadTracker?.checkThreadLeak(gradle)
}
}
cleanup(gradle.gradleVersion)
// checking thread leaks only then cleaning up
threadTracker?.checkThreadLeak(gradle)
threadTracker = null
gradle.removeListener(this)
......@@ -87,14 +70,36 @@ class FinishBuildListener(pluginClassLoader: ClassLoader?, val startMemory: Long
class CompilerServicesCleanup(private var pluginClassLoader: ClassLoader?) {
val log = Logging.getLogger(this.javaClass)
fun invoke() {
operator fun invoke(gradleVersion: String) {
assert(pluginClassLoader != null)
log.kotlinDebug("compiler services cleanup")
// TODO: remove ZipFileCache cleanup after switching to recent idea libs
stopZipFileCache()
stopJobScheduler()
// clearing jar cache to avoid problems like KT-9440 (unable to clean/rebuild a project due to locked jar file)
// problem is known to happen only on windows - the reason (seems) related to http://bugs.java.com/view_bug.do?bug_id=6357433
// clean cache only when running on windows
if (SystemUtils.IS_OS_WINDOWS) {
cleanJarCache()
}
// making cleanup of static objects only on recognized versions of gradle and if version < 2.4
// otherwise it may cause problems e.g. with JobScheduler on subsequent runs
// this strategy may lead to memory leaks, but prevent crashes due to destroyed JobScheduler
// the reason for the strategy is the following:
// gradle < 2.4 has problems with plugin reuse in the daemon: new calls to the plugin are made with a new classloader
// for every new build. With statically initialized daemons like JobScheduler that leads to big leaks of classloaders and classes,
// therefore to reduce leaks JobScheduler (and deprecated ZipFileCache for now) should be stopped)
// It should be noted that because of this behavior there are no benefits of using daemon in these versions.
// Starting from 2.4 gradle using cached classloaders, that leads to effective class reusing in the daemon, but
// in that case premature stopping of the static daemons may lead to crashes.
comparableVersionStr(gradleVersion)?.let {
log.kotlinDebug("detected gradle version $it")
if (it < comparableVersionStr("2.4")!!) {
// TODO: remove ZipFileCache cleanup after switching to recent idea libs
stopZipFileCache()
stopJobScheduler()
}
}
pluginClassLoader = null
}
......@@ -128,4 +133,24 @@ class CompilerServicesCleanup(private var pluginClassLoader: ClassLoader?) {
log.kotlinDebug("Call $shortName.$methodName()")
shutdownMethod.invoke(null)
}
private fun cleanJarCache() {
val zipHandlerClass = pluginClassLoader!!.loadClass("com.intellij.openapi.vfs.impl.ZipHandler")
val privateCacheField = zipHandlerClass.getDeclaredField("ourZipFileFileAccessorCache")
privateCacheField.isAccessible = true
privateCacheField.get(null)?.let {
val innerCache = privateCacheField.type.getDeclaredField("myCache")
innerCache.isAccessible = true
innerCache.get(it)?.let {
val clearMethod = innerCache.type.getMethod("clear")
if (clearMethod != null) {
clearMethod.invoke(it)
log.kotlinDebug("successfuly cleared ZipHandler.ourZipFileFileAccessorCache.myCache")
}
else {
log.kotlinDebug("unable to access ZipHandler.ourZipFileFileAccessorCache.myCache.clear")
}
} ?: log.kotlinDebug("unable to access ZipHandler.ourZipFileFileAccessorCache.myCache (${innerCache.get(it)})")
} ?: log.kotlinDebug("unable to access ZipHandler.ourZipFileFileAccessorCache (${privateCacheField.get(null)})")
}
}
......@@ -71,6 +71,10 @@ class KotlinGradleIT: BaseGradleIT() {
}
}
// testing that nothing remains locked by daemon, see KT-9440
project.build(userVariantArg, "clean", options = BaseGradleIT.BuildOptions(withDaemon = true)) {
assertSuccessful()
}
}
finally {
exitTestDaemon()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册