play-java-angular-seed icon indicating copy to clipboard operation
play-java-angular-seed copied to clipboard

Ubuntu/Linux: Java 17, Unrecognized VM option 'MaxPermSize=256m'

Open thedailycommute opened this issue 1 year ago • 1 comments

A few days ago I downloaded a zipped version of the play-java-angular-seed to use as guidance for an (eventual) upgrade/rewrite of a legacy Angular 2/Play 2.5 application. However, when I tried to run the version of sbt included in the distribution, I was immediately presented with the following error:

thedailycommute:~/pling/play-java-angular-seed-main$ ./sbt -v
[process_args] java_version = '17.0.12'
# Executing command line:
java
-Xms1024m
-Xmx1024m
-XX:ReservedCodeCacheSize=128m
-XX:MaxPermSize=256m
-jar
/home/thedailycommute/pling/play-java-angular-seed-main/sbt-dist/bin/sbt-launch.jar

Unrecognized VM option 'MaxPermSize=256m'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

I tracked the error to the file sbt-launch-lib.bash, specifically to the get_mem_opts() function, line 92, which reads:

local class_metadata_opt=$([[ "$java_version" < "1.8" ]] && echo "MaxPermSize" || echo "MaxMetaspaceSize")

The problem occurs because Bash is performing a lexicographical comparison of the version strings, and (incorrectly in this case) returns "true", leading to the VM error because MaxPermSize was removed in versions of Java >= 1.8.

I humbly suggest the following changes:

  1. Add a new function, version_compare(), to allow version strings to be correctly compared.
# Function to compare two version numbers
# Returns:
# -1 if version1 < version2
#  0 if version1 == version2
#  1 if version1 > version2
version_compare() {
  # Convert versions to arrays of numbers
  IFS=. read -r -a version1 <<< "$1"
  IFS=. read -r -a version2 <<< "$2"

  # Compare each segment of the versions
  for ((i = 0; i < ${#version1[@]} || i < ${#version2[@]}; i++)); do
    v1=${version1[i]:-0}
    v2=${version2[i]:-0}
    if ((v1 > v2)); then
      return 1  # version1 is greater
    elif ((v1 < v2)); then
      return -1 # version1 is less
    fi
  done

  # If we reached here, versions are equal
  return 0
}
  1. Replace the single comparison line in get_mem_opts() with:
    # Define class metadata option based on Java version
    local class_metadata_opt=$(
      version_compare "$java_version" "1.8"
      case $? in
        -1) echo "MaxPermSize" ;; # Less than 1.8
        0|1) echo "MaxMetaspaceSize" ;; # 1.8 or greater
      esac
    )
  1. Replace the checkJava() function with:
# Detect that we have java installed.
checkJava() {
  local required_version="$1"
  
  # Check if Java is installed
  if [[ -z "$java_version" ]]; then
    echo
    echo "No Java installation was detected."
    echo "Please go to http://www.java.com/getjava/ and download"
    echo
    exit 1
  fi

  # Compare the installed version with the required version
  version_compare "$java_version" "$required_version"
  case $? in
    -1)
      echo
      echo "The Java installation you have is not up to date."
      echo "$script_name requires at least version $required_version+, you have"
      echo "version $java_version."
      echo
      echo "Please go to http://www.java.com/getjava/ and download"
      echo "a valid Java Runtime and install before running $script_name."
      echo
      exit 1
      ;;
    0|1)
      # Version is sufficient
      return 0
      ;;
  esac
}

I have attached a fully updated version (with a .txt extension uploading purposes) to simplify validation.

I should also add that the changes were developed with the aid of ChatGPT (because it's been several decades since I last edited a bash script :-)

sbt-launch-lib.bash.txt

thedailycommute avatar Aug 02 '24 11:08 thedailycommute