Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,23 @@ class RestPropMapper<C, out P : KMutableProperty1<C, MutableCollection<String>>>
}


private class KeepPropMapper<C, V, P : KMutableProperty1<C, V>>(private val delegate: PropMapper<C, V, P>, private val predicate: (V) -> Boolean)
: PropMapper<C, V, P>(dest = delegate.dest, prop = delegate.prop, names = delegate.names,
fromString = delegate.fromString, toString = delegate.toString,
skipIf = delegate.skipIf, mergeDelimiter = delegate.mergeDelimiter) {
override fun apply(s: String) {
if (!predicate(delegate.prop.get(delegate.dest))) {
super.apply(s)
}
}
}


internal fun <C, V, P : KMutableProperty1<C, V>> PropMapper<C, V, P>.keepIf(predicate: (V) -> Boolean): PropMapper<C, V, P> {
return KeepPropMapper(this, predicate)
}


// helper function combining find with map, useful for the cases then there is a calculation performed in find, which is nice to return along with
// found value; mappingPredicate should return the pair of boolean compare predicate result and transformation value, we want to get along with found value
inline fun <T, R : Any> Iterable<T>.findWithTransform(mappingPredicate: (T) -> Pair<Boolean, R?>): R? {
Expand Down Expand Up @@ -179,19 +196,28 @@ fun Iterable<String>.filterExtractProps(vararg groups: OptionsGroup, prefix: Str


data class DaemonJVMOptions(
var maxMemory: String = "",
var maxHeapSize: String = "",
var maxRam: String = "",
var maxRamFraction: String = "",
var maxRamPercentage: String = "",
var maxMetaspaceSize: String = "",
var reservedCodeCacheSize: String = "320m",
var jvmParams: MutableCollection<String> = arrayListOf()
) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>>
get() = listOf(StringPropMapper(this, DaemonJVMOptions::maxMemory, listOf("Xmx"), mergeDelimiter = ""),
StringPropMapper(this, DaemonJVMOptions::maxMetaspaceSize, listOf("XX:MaxMetaspaceSize"), mergeDelimiter = "="),
StringPropMapper(this, DaemonJVMOptions::reservedCodeCacheSize, listOf("XX:ReservedCodeCacheSize"), mergeDelimiter = "="),
get() = listOf(StringPropMapper(this, DaemonJVMOptions::maxHeapSize, listOf("Xmx"), mergeDelimiter = "").keepIf { hasMaxHeapSize },
StringPropMapper(this, DaemonJVMOptions::maxRam, listOf("XX:MaxRAM"), mergeDelimiter = "=").keepIf { hasMaxHeapSize },
StringPropMapper(this, DaemonJVMOptions::maxRamFraction, listOf("XX:MaxRAMFraction"), mergeDelimiter = "=").keepIf { hasMaxHeapSize },
StringPropMapper(this, DaemonJVMOptions::maxRamPercentage, listOf("XX:MaxRAMPercentage"), mergeDelimiter = "=").keepIf { hasMaxHeapSize },
StringPropMapper(this, DaemonJVMOptions::maxMetaspaceSize, listOf("XX:MaxMetaspaceSize"), mergeDelimiter = "=").keepIf { it.isNotBlank() },
StringPropMapper(this, DaemonJVMOptions::reservedCodeCacheSize, listOf("XX:ReservedCodeCacheSize"), mergeDelimiter = "=").keepIf { it.isNotBlank() },
restMapper)

val restMapper: RestPropMapper<*, *>
get() = RestPropMapper(this, DaemonJVMOptions::jvmParams)

internal val hasMaxHeapSize: Boolean
get() = maxHeapSize.isNotBlank() || maxRam.isNotBlank() || maxRamFraction.isNotBlank() || maxRamPercentage.isNotBlank()
}


Expand Down Expand Up @@ -289,11 +315,11 @@ fun configureDaemonJVMOptions(opts: DaemonJVMOptions,
val otherArgs = jvmArguments.filterExtractProps(targetOptions.mappers, prefix = "-")

if (inheritMemoryLimits) {
if (opts.maxMemory.isBlank()) {
if (!opts.hasMaxHeapSize) {
val maxMemBytes = Runtime.getRuntime().maxMemory()
// rounding up
val maxMemMegabytes = maxMemBytes / (1024 * 1024) + if (maxMemBytes % (1024 * 1024) == 0L) 0 else 1
opts.maxMemory = "${maxMemMegabytes}m"
opts.maxHeapSize = "${maxMemMegabytes}m"
}
}

Expand Down Expand Up @@ -383,7 +409,8 @@ private fun String.memToBytes(): Long? =


private val daemonJVMOptionsMemoryProps =
listOf(DaemonJVMOptions::maxMemory, DaemonJVMOptions::maxMetaspaceSize, DaemonJVMOptions::reservedCodeCacheSize)
listOf(DaemonJVMOptions::maxHeapSize, DaemonJVMOptions::maxMetaspaceSize, DaemonJVMOptions::reservedCodeCacheSize,
DaemonJVMOptions::maxRam, DaemonJVMOptions::maxRamFraction, DaemonJVMOptions::maxRamPercentage)

infix fun DaemonJVMOptions.memorywiseFitsInto(other: DaemonJVMOptions): Boolean =
daemonJVMOptionsMemoryProps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ abstract class BaseDaemonSessionTest {

open val defaultDaemonJvmOptions
get() = DaemonJVMOptions(
maxMemory = "384m"
maxHeapSize = "384m"
)

@AfterEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
additionalArgs.add("D${CompilerSystemProperties.COMPILE_DAEMON_LOG_PATH_PROPERTY.property}=\"${logFile.loggerCompatiblePath}\"")
}
args.forEach { additionalArgs.add(it) }
val baseOpts = if (xmx > 0) DaemonJVMOptions(maxMemory = "${xmx}m") else DaemonJVMOptions()
val baseOpts = if (xmx > 0) DaemonJVMOptions(maxHeapSize = "${xmx}m") else DaemonJVMOptions()
return configureDaemonJVMOptions(
baseOpts,
*additionalArgs.toTypedArray(),
Expand Down Expand Up @@ -158,16 +158,16 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
try {
System.setProperty(CompilerSystemProperties.COMPILE_DAEMON_JVM_OPTIONS_PROPERTY.property, "-aaa,-bbb\\,ccc,-ddd,-Xmx200m,-XX:MaxMetaspaceSize=10k,-XX:ReservedCodeCacheSize=100,-xxx\\,yyy")
val opts = configureDaemonJVMOptions(inheritMemoryLimits = false, inheritAdditionalProperties = false, inheritOtherJvmOptions = false)
assertEquals("200m", opts.maxMemory)
assertEquals("200m", opts.maxHeapSize)
assertEquals("10k", opts.maxMetaspaceSize)
assertEquals("100", opts.reservedCodeCacheSize)
assertEquals(arrayListOf("aaa", "bbb,ccc", "ddd", "xxx,yyy", "ea"), opts.jvmParams)

System.setProperty(CompilerSystemProperties.COMPILE_DAEMON_JVM_OPTIONS_PROPERTY.property, "-Xmx300m,-XX:MaxMetaspaceSize=10k,-XX:ReservedCodeCacheSize=100")
val opts2 = configureDaemonJVMOptions(inheritMemoryLimits = false, inheritAdditionalProperties = false, inheritOtherJvmOptions = false)
assertEquals("300m", opts2.maxMemory)
assertEquals("300m", opts2.maxHeapSize)
assertEquals( -1, DaemonJVMOptionsMemoryComparator().compare(opts, opts2))
assertEquals("300m", listOf(opts, opts2).maxWithOrNull(DaemonJVMOptionsMemoryComparator())?.maxMemory)
assertEquals("300m", listOf(opts, opts2).maxWithOrNull(DaemonJVMOptionsMemoryComparator())?.maxHeapSize)

val myXmxParam = ManagementFactory.getRuntimeMXBean().inputArguments.first { it.startsWith("-Xmx") }
TestCase.assertNotNull(myXmxParam)
Expand All @@ -176,7 +176,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
val opts3 = configureDaemonJVMOptions(inheritMemoryLimits = true,
inheritOtherJvmOptions = true,
inheritAdditionalProperties = false)
assertEquals(myXmxVal, opts3.maxMemory)
assertEquals(myXmxVal, opts3.maxHeapSize)
}
finally {
restoreSystemProperty(CompilerSystemProperties.COMPILE_DAEMON_JVM_OPTIONS_PROPERTY.property, backupJvmOptions)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

package org.jetbrains.kotlin.daemon.common

import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals

@DisplayName("configureDaemonJVMOptions")
class ConfigureDaemonJvmOptionsTest {

@Test
fun `inheritMemoryLimits should produce default maxMemory`() {
val opts = configureDaemonJVMOptions(
DaemonJVMOptions(),
inheritMemoryLimits = true,
inheritOtherJvmOptions = true,
inheritAdditionalProperties = true
)
assertNotEquals("", opts.maxHeapSize)
assertEquals("", opts.maxRam)
assertEquals("", opts.maxRamFraction)
assertEquals("", opts.maxRamPercentage)
}

@Test
fun `inheritMemoryLimits should keep maxMemory`() {
val opts = configureDaemonJVMOptions(
DaemonJVMOptions(maxHeapSize = "maxMemoryValue"),
inheritMemoryLimits = true,
inheritOtherJvmOptions = true,
inheritAdditionalProperties = true
)
assertEquals("maxMemoryValue", opts.maxHeapSize)
assertEquals("", opts.maxRam)
assertEquals("", opts.maxRamFraction)
assertEquals("", opts.maxRamPercentage)
}

@Test
fun `inheritMemoryLimits should keep maxRam`() {
val opts = configureDaemonJVMOptions(
DaemonJVMOptions(maxRam = "maxRamValue"),
inheritMemoryLimits = true,
inheritOtherJvmOptions = true,
inheritAdditionalProperties = true
)
assertEquals("", opts.maxHeapSize)
assertEquals("maxRamValue", opts.maxRam)
assertEquals("", opts.maxRamFraction)
assertEquals("", opts.maxRamPercentage)
}

@Test
fun `inheritMemoryLimits should keep maxRamFraction`() {
val opts = configureDaemonJVMOptions(
DaemonJVMOptions(maxRamFraction = "maxRamFractionValue"),
inheritMemoryLimits = true,
inheritOtherJvmOptions = true,
inheritAdditionalProperties = true
)
assertEquals("", opts.maxHeapSize)
assertEquals("", opts.maxRam)
assertEquals("maxRamFractionValue", opts.maxRamFraction)
assertEquals("", opts.maxRamPercentage)
}

@Test
fun `inheritMemoryLimits should keep maxRamPercentage`() {
val opts = configureDaemonJVMOptions(
DaemonJVMOptions(maxRamPercentage = "maxRamPercentageValue"),
inheritMemoryLimits = true,
inheritOtherJvmOptions = true,
inheritAdditionalProperties = true
)
assertEquals("", opts.maxHeapSize)
assertEquals("", opts.maxRam)
assertEquals("", opts.maxRamFraction)
assertEquals("maxRamPercentageValue", opts.maxRamPercentage)
}

@Test
fun `inheritMemoryLimits should keep all limits`() {
val opts = configureDaemonJVMOptions(
DaemonJVMOptions(
maxHeapSize = "maxMemoryValue",
maxRam = "maxRamValue",
maxRamFraction = "maxRamFractionValue",
maxRamPercentage = "maxRamPercentageValue"
),
inheritMemoryLimits = true,
inheritOtherJvmOptions = true,
inheritAdditionalProperties = true
)
assertEquals("maxMemoryValue", opts.maxHeapSize)
assertEquals("maxRamValue", opts.maxRam)
assertEquals("maxRamFractionValue", opts.maxRamFraction)
assertEquals("maxRamPercentageValue", opts.maxRamPercentage)
}
}