From d0efd4083fe8b66b4c00cb8148f1c3dc8fe72319 Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Fri, 12 Feb 2016 20:41:19 +0100 Subject: [PATCH] Call System.gc only when when log level is debug, fixes KT-11022 --- ...ildListener.kt => CleanUpBuildListener.kt} | 44 +++++++++++++------ .../gradle/plugin/KotlinPluginWrapper.kt | 18 +++----- .../gradle/BasicKotlinGradlePluginIT.kt | 4 +- .../kotlin/gradle/KotlinGradlePluginIT.kt | 16 ++++++- 4 files changed, 52 insertions(+), 30 deletions(-) rename libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/{FinishBuildListener.kt => CleanUpBuildListener.kt} (79%) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CleanUpBuildListener.kt similarity index 79% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CleanUpBuildListener.kt index 4979d77a8c0..840726d1eb5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CleanUpBuildListener.kt @@ -21,12 +21,7 @@ import org.gradle.BuildAdapter import org.gradle.BuildResult import org.gradle.api.logging.Logging import java.util.concurrent.ScheduledExecutorService - -internal fun getUsedMemoryKb(): Long { - System.gc() - val rt = Runtime.getRuntime() - return (rt.totalMemory() - rt.freeMemory()) / 1024 -} +import kotlin.properties.Delegates private fun comparableVersionStr(version: String) = @@ -39,13 +34,23 @@ private fun comparableVersionStr(version: String) = ?.let { if (it.all { (it?.value?.length ?: 0).let { it > 0 && it < 4 }}) it else null } ?.joinToString(".", transform = { it!!.value.padStart(3, '0') }) +class CleanUpBuildListener(pluginClassLoader: ClassLoader) : BuildAdapter() { + companion object { + const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()" + } -class FinishBuildListener(pluginClassLoader: ClassLoader, val startMemory: Long) : BuildAdapter() { - val log = Logging.getLogger(this.javaClass) - + private val log = Logging.getLogger(this.javaClass) private var threadTracker: ThreadTracker? = ThreadTracker() - private val cleanup = CompilerServicesCleanup(pluginClassLoader) + private var startMemory: Long? = null + + // There is function with the same name in BuildAdapter, + // but it is called before any plugin can attach build listener + fun buildStarted() { + if (log.isDebugEnabled) { + startMemory = getUsedMemoryKb()!! + } + } override fun buildFinished(result: BuildResult?) { log.kotlinDebug("Build finished listener") @@ -60,9 +65,22 @@ class FinishBuildListener(pluginClassLoader: ClassLoader, val startMemory: Long) gradle.removeListener(this) } - // the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes - // but on subsequent runs in the daemon it should be rather small, then the classes are actually reused by the daemon (see above) - getUsedMemoryKb().let { log.kotlinDebug("[PERF] Used memory after build: $it kb (${"%+d".format(it - startMemory)} kb)") } + startMemory?.let { startMemoryCopy -> + getUsedMemoryKb()?.let { endMemory -> + // the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes + // but on subsequent runs in the daemon it should be rather small, then the classes are actually reused by the daemon (see above) + log.kotlinDebug("[PERF] Used memory after build: $endMemory kb (difference since build start: ${"%+d".format(endMemory - startMemoryCopy)} kb)") + } + } + } + + private fun getUsedMemoryKb(): Long? { + if (!log.isDebugEnabled) return null + + log.lifecycle(FORCE_SYSTEM_GC_MESSAGE) + System.gc() + val rt = Runtime.getRuntime() + return (rt.totalMemory() - rt.freeMemory()) / 1024 } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index d92003dcb45..b169d559e67 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -2,18 +2,10 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.artifacts.dsl.DependencyHandler -import org.gradle.api.artifacts.ConfigurationContainer -import java.net.URL -import org.gradle.api.logging.Logging -import java.util.Properties -import java.io.FileNotFoundException import org.gradle.api.initialization.dsl.ScriptHandler -import org.gradle.api.invocation.Gradle import org.gradle.api.logging.Logger -import java.lang.reflect.Method +import org.gradle.api.logging.Logging import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider -import java.net.URLClassLoader // TODO: simplify: the complicated structure is a leftover from dynamic loading of plugin core, could be significantly simplified now @@ -21,9 +13,10 @@ abstract class KotlinBasePluginWrapper: Plugin { val log = Logging.getLogger(this.javaClass) - public override fun apply(project: Project) { - - val startMemory = getUsedMemoryKb() + override fun apply(project: Project) { + val cleanUpBuildListener = CleanUpBuildListener(this.javaClass.classLoader) + cleanUpBuildListener.buildStarted() + project.gradle.addBuildListener(cleanUpBuildListener) val sourceBuildScript = findSourceBuildScript(project) if (sourceBuildScript == null) { @@ -38,7 +31,6 @@ abstract class KotlinBasePluginWrapper: Plugin { val plugin = getPlugin(this.javaClass.classLoader, sourceBuildScript) plugin.apply(project) - project.gradle.addBuildListener(FinishBuildListener(this.javaClass.classLoader, startMemory)) } protected abstract fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler): Plugin diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BasicKotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BasicKotlinGradlePluginIT.kt index c3d9b796260..0afc91a6c9c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BasicKotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/BasicKotlinGradlePluginIT.kt @@ -1,9 +1,7 @@ package org.jetbrains.kotlin.gradle -import org.junit.Test -import org.jetbrains.kotlin.gradle.BaseGradleIT.Project import org.gradle.api.logging.LogLevel -import org.junit.Ignore +import org.junit.Test class SimpleKotlinGradleIT : BaseGradleIT() { diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt index f5f2b8ccef5..4a78441b6f1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt @@ -1,6 +1,7 @@ package org.jetbrains.kotlin.gradle -import org.jetbrains.kotlin.gradle.BaseGradleIT.Project +import org.gradle.api.logging.LogLevel +import org.jetbrains.kotlin.gradle.plugin.CleanUpBuildListener import org.junit.Test class KotlinGradleIT: BaseGradleIT() { @@ -81,6 +82,19 @@ class KotlinGradleIT: BaseGradleIT() { } } + @Test + fun testLogLevelForceGC() { + val debugProject = Project("simpleProject", "1.12", minLogLevel = LogLevel.DEBUG) + debugProject.build("build") { + assertContains(CleanUpBuildListener.FORCE_SYSTEM_GC_MESSAGE) + } + + val infoProject = Project("simpleProject", "1.12", minLogLevel = LogLevel.INFO) + infoProject.build("clean", "build") { + assertNotContains(CleanUpBuildListener.FORCE_SYSTEM_GC_MESSAGE) + } + } + @Test fun testKotlinClasspath() { Project("classpathTest", "1.6").build("build") {