Search code examples
javashellgradlejvmjvm-arguments

Can I use "kill %p" in a script being called by JVM on OOM


I'm still getting ready to test this, but I would like some input before I go any further. I am setting up out of memory handling with JVM in gradle. Part of that involves creating a restart script to be called on OOM by the JVM options.

The way it works is that I first

task appStartScripts(type: CreateStartScripts) {
    def tplName = 'startBinTemplate.sh'
    assert project.file(tplName).exists()
    unixStartScriptGenerator.template = resources.text.fromFile(tplName)
    defaultJvmOpts = [
                      "-XX:+HeapDumpOnOutOfMemoryError",
                      "-XX:HeapDumpPath=\$HOME/apps/log/",
                      "-XX:OnOutOfMemoryError=./restart.sh",

                      "-Xms1G", "-Xmx2G",
                      "-Dapp.name=${rootProject.name}"]

    dependsOn shadowJar
    applicationName = 'start'
    defaultJvmOpts += ["-Dspring.profiles.active=ENV_VARIABLE"]
    classpath = startShadowScripts.classpath
    mainClassName = startShadowScripts.mainClassName
    outputDir = new File(project.buildDir, 'scriptsShadow')

    doLast {
        unixScript.text = unixScript.text.replace('\\$HOME', '\'"$HOME"\'')
        unixScript.text = unixScript.text.replace('ENV_VARIABLE', '\'"$1"\'')
    }
}

In theory, on an out of memory error, the restart script will be called:

#!/usr/bin/env sh

kill -9 %p
sleep 5
./start.sh

Which will kill the process, sleep for 5 seconds, and then use the start script to restart it. My question is on the %p argument for kill. Can I use it in the script in this context, as opposed to the JVM argument itself? My understanding is that it should be passed to the JVM argument, which will then use it to pass in the services PID to kill the service. In my case, I'm using it in a script as trying to pass kill -9 %p to $JVM_ARGS causes an error on startup:

Unrecognized option: -9

whereas passing in a script to be called does not seem to cause any issues.

I'm still setting up a test case for this, but I wanted to ask:

  1. Has anyone done this? What was your experience?
  2. Is there a better way for me to do this, that gets around the error?

Solution

  • %p placeholder is resolved by the JVM when OnOutOfMemoryError is executed. You would have to use in the command -XX:OnOutOfMemoryError=./restart.sh %p and then read it from $1 in the restart.sh.

    Modern JVM has ExitOnOutOfMemoryError option which behaves same as OnOutOfMemoryError="kill -9 %p" (see JDK-8152669). You can combine this with a bash script shown in this answer:

    #!/bin/sh
    
    while true ; do
      java -XX:+ExitOnOutOfMemoryError -jar application.jar
      sleep 5
    done