mirror of https://github.com/sbt/sbt.git
Merge pull request #1534 from havocp/wip/havocp-server-mem
Set JVM memory options for server applications
This commit is contained in:
commit
ad34bf77e7
|
|
@ -136,10 +136,7 @@ object ServerLauncher {
|
|||
if (System.getenv("SBT_SERVER_SAVE_TEMPS") eq null)
|
||||
launchConfig.deleteOnExit()
|
||||
LaunchConfiguration.save(config, launchConfig)
|
||||
val jvmArgs: List[String] = serverConfig.jvmArgs map readLines match {
|
||||
case Some(args) => args
|
||||
case None => Nil
|
||||
}
|
||||
val jvmArgs: List[String] = serverJvmArgs(currentDirectory, serverConfig)
|
||||
val cmd: List[String] =
|
||||
("java" :: jvmArgs) ++
|
||||
("-jar" :: defaultLauncherLookup.getCanonicalPath :: s"@load:${launchConfig.toURI.toURL.toString}" :: Nil)
|
||||
|
|
@ -211,6 +208,74 @@ object ServerLauncher {
|
|||
finally reader.close()
|
||||
}
|
||||
|
||||
// None = couldn't figure it out
|
||||
def javaIsAbove(currentDirectory: File, version: Int): Option[Boolean] = try {
|
||||
val pb = new java.lang.ProcessBuilder()
|
||||
// hopefully "java -version" is a lot faster than booting the full JVM.
|
||||
// not sure how else we can do this.
|
||||
pb.command("java", "-version")
|
||||
pb.directory(currentDirectory)
|
||||
val process = pb.start()
|
||||
try {
|
||||
process.getOutputStream.close()
|
||||
process.getInputStream.close()
|
||||
val stderr = new java.io.LineNumberReader(new java.io.InputStreamReader(process.getErrorStream))
|
||||
// Looking for the first line which is `java version "1.7.0_60"` or similar
|
||||
val lineOption = try Option(stderr.readLine()) finally stderr.close()
|
||||
val pattern = java.util.regex.Pattern.compile("""java version "[0-9]+\.([0-9]+)\..*".*""")
|
||||
lineOption flatMap { line =>
|
||||
val matcher = pattern.matcher(line)
|
||||
if (matcher.matches()) {
|
||||
try Some(Integer.parseInt(matcher.group(1)) > version) catch { case NonFatal(_) => None }
|
||||
} else {
|
||||
System.err.println(s"Failed to parse version from 'java -version' output '$line'")
|
||||
None
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
process.destroy()
|
||||
try { process.waitFor() } catch { case NonFatal(_) => }
|
||||
}
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
// both process.start and reading the output streams can throw IOException.
|
||||
// all OS exceptions from process.start are supposed to be IOException.
|
||||
System.err.println(s"Failed to run 'java -version': ${e.getClass.getName}: ${e.getMessage}")
|
||||
None
|
||||
}
|
||||
|
||||
def serverJvmArgs(currentDirectory: File, serverConfig: ServerConfiguration): List[String] =
|
||||
serverJvmArgs(currentDirectory, serverConfig.jvmArgs map readLines getOrElse Nil)
|
||||
|
||||
final val memOptPrefixes = List("-Xmx", "-Xms", "-XX:MaxPermSize", "-XX:PermSize", "-XX:ReservedCodeCacheSize", "-XX:MaxMetaspaceSize", "-XX:MetaspaceSize")
|
||||
|
||||
final val defaultMinHeapM = 256
|
||||
final val defaultMaxHeapM = defaultMinHeapM * 4
|
||||
final val defaultMinPermM = 64
|
||||
final val defaultMaxPermM = defaultMinPermM * 4
|
||||
|
||||
// this is separate just for the test suite
|
||||
def serverJvmArgs(currentDirectory: File, baseArgs: List[String]): List[String] = {
|
||||
// ignore blank lines
|
||||
val trimmed = baseArgs.map(_.trim).filterNot(_.isEmpty)
|
||||
// If the user config has provided ANY memory options we bail out and do NOT add
|
||||
// any defaults. This means people can always fix our mistakes, and it avoids
|
||||
// issues where the JVM refuses to start because of (for example) min size greater
|
||||
// than max size. We don't want to deal with coordinating our changes with the
|
||||
// user configuration.
|
||||
def isMemoryOption(s: String) = memOptPrefixes.exists(s.startsWith(_))
|
||||
if (trimmed.exists(isMemoryOption(_)))
|
||||
trimmed
|
||||
else {
|
||||
val permOptions = javaIsAbove(currentDirectory, 7) match {
|
||||
case Some(true) => List(s"-XX:MetaspaceSize=${defaultMinPermM}m", s"-XX:MaxMetaspaceSize=${defaultMaxPermM}m")
|
||||
case Some(false) => List(s"-XX:PermSize=${defaultMinPermM}m", s"-XX:MaxPermSize=${defaultMaxPermM}m")
|
||||
case None => Nil // don't know what we're doing, so don't set options
|
||||
}
|
||||
s"-Xms${defaultMinHeapM}m" :: s"-Xmx${defaultMaxHeapM}m" :: (permOptions ++ trimmed)
|
||||
}
|
||||
}
|
||||
|
||||
def defaultLauncherLookup: File =
|
||||
try {
|
||||
val classInLauncher = classOf[AppConfiguration]
|
||||
|
|
|
|||
|
|
@ -48,5 +48,42 @@ object ServerLocatorTest extends Specification {
|
|||
finally inputStream.close()
|
||||
result must equalTo(Some(expected))
|
||||
}
|
||||
"determine a JVM version" in {
|
||||
withTemporaryDirectory { dir =>
|
||||
// javaIs8OrAbove returns None for pathological situations
|
||||
// (weird errors running java -version or something),
|
||||
// but when testing sbt we should not be in such a situation.
|
||||
val determined = ServerLauncher.javaIsAbove(dir, 7)
|
||||
determined must beSome
|
||||
}
|
||||
}
|
||||
"have JVM memory defaults" in {
|
||||
withTemporaryDirectory { dir =>
|
||||
val defaults = ServerLauncher.serverJvmArgs(dir, Nil)
|
||||
defaults must contain(beEqualTo("-Xms256m"))
|
||||
defaults must contain(beEqualTo("-Xmx1024m"))
|
||||
if (ServerLauncher.javaIsAbove(dir, 7).getOrElse(false)) {
|
||||
defaults must contain(beEqualTo("-XX:MetaspaceSize=64m"))
|
||||
defaults must contain(beEqualTo("-XX:MaxMetaspaceSize=256m"))
|
||||
} else {
|
||||
defaults must contain(beEqualTo("-XX:PermSize=64m"))
|
||||
defaults must contain(beEqualTo("-XX:MaxPermSize=256m"))
|
||||
}
|
||||
}
|
||||
}
|
||||
"leave user-specified memory options alone" in {
|
||||
withTemporaryDirectory { dir =>
|
||||
val args = ServerLauncher.serverJvmArgs(dir, List("-Xmx4321m"))
|
||||
args must contain(beEqualTo("-Xmx4321m"))
|
||||
args must not contain (beEqualTo("-Xms256m"))
|
||||
args must not contain (beEqualTo("-Xmx1024m"))
|
||||
}
|
||||
}
|
||||
"ignore whitespace in jvm args file" in {
|
||||
withTemporaryDirectory { dir =>
|
||||
val args = ServerLauncher.serverJvmArgs(dir, List("", " ", " -Xmx4321m ", " ", ""))
|
||||
args must equalTo(List("-Xmx4321m"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue