Call System.gc only when when log level is debug, fixes KT-11022

This commit is contained in:
Alexey Tsvetkov
2016-02-12 20:41:19 +01:00
committed by Ilya Chernikov
parent bc5110550a
commit d0efd4083f
4 changed files with 52 additions and 30 deletions
@@ -21,12 +21,7 @@ import org.gradle.BuildAdapter
import org.gradle.BuildResult import org.gradle.BuildResult
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledExecutorService
import kotlin.properties.Delegates
internal fun getUsedMemoryKb(): Long {
System.gc()
val rt = Runtime.getRuntime()
return (rt.totalMemory() - rt.freeMemory()) / 1024
}
private fun comparableVersionStr(version: String) = 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 } ?.let { if (it.all { (it?.value?.length ?: 0).let { it > 0 && it < 4 }}) it else null }
?.joinToString(".", transform = { it!!.value.padStart(3, '0') }) ?.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() { private val log = Logging.getLogger(this.javaClass)
val log = Logging.getLogger(this.javaClass)
private var threadTracker: ThreadTracker? = ThreadTracker() private var threadTracker: ThreadTracker? = ThreadTracker()
private val cleanup = CompilerServicesCleanup(pluginClassLoader) 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?) { override fun buildFinished(result: BuildResult?) {
log.kotlinDebug("Build finished listener") log.kotlinDebug("Build finished listener")
@@ -60,9 +65,22 @@ class FinishBuildListener(pluginClassLoader: ClassLoader, val startMemory: Long)
gradle.removeListener(this) gradle.removeListener(this)
} }
// the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes startMemory?.let { startMemoryCopy ->
// 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 { endMemory ->
getUsedMemoryKb().let { log.kotlinDebug("[PERF] Used memory after build: $it kb (${"%+d".format(it - startMemory)} kb)") } // 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
} }
} }
@@ -2,18 +2,10 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project 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.initialization.dsl.ScriptHandler
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logger 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 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 // 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<Project> {
val log = Logging.getLogger(this.javaClass) val log = Logging.getLogger(this.javaClass)
public override fun apply(project: Project) { override fun apply(project: Project) {
val cleanUpBuildListener = CleanUpBuildListener(this.javaClass.classLoader)
val startMemory = getUsedMemoryKb() cleanUpBuildListener.buildStarted()
project.gradle.addBuildListener(cleanUpBuildListener)
val sourceBuildScript = findSourceBuildScript(project) val sourceBuildScript = findSourceBuildScript(project)
if (sourceBuildScript == null) { if (sourceBuildScript == null) {
@@ -38,7 +31,6 @@ abstract class KotlinBasePluginWrapper: Plugin<Project> {
val plugin = getPlugin(this.javaClass.classLoader, sourceBuildScript) val plugin = getPlugin(this.javaClass.classLoader, sourceBuildScript)
plugin.apply(project) plugin.apply(project)
project.gradle.addBuildListener(FinishBuildListener(this.javaClass.classLoader, startMemory))
} }
protected abstract fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler): Plugin<Project> protected abstract fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler): Plugin<Project>
@@ -1,9 +1,7 @@
package org.jetbrains.kotlin.gradle package org.jetbrains.kotlin.gradle
import org.junit.Test
import org.jetbrains.kotlin.gradle.BaseGradleIT.Project
import org.gradle.api.logging.LogLevel import org.gradle.api.logging.LogLevel
import org.junit.Ignore import org.junit.Test
class SimpleKotlinGradleIT : BaseGradleIT() { class SimpleKotlinGradleIT : BaseGradleIT() {
@@ -1,6 +1,7 @@
package org.jetbrains.kotlin.gradle 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 import org.junit.Test
class KotlinGradleIT: BaseGradleIT() { 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 @Test
fun testKotlinClasspath() { fun testKotlinClasspath() {
Project("classpathTest", "1.6").build("build") { Project("classpathTest", "1.6").build("build") {