Multiple JDKs on Mac
Overview
With the new release model of Java
, now new Java version is released after
every 6 months. For learning and experimenting with different JDK versions it is
mandatory to install and manage multiple versions. In this article we will learn
how easily we can manage multiple JDKs on Mac OS.
Install Location
When an Oracle JDK is installed in Mac OS, it is installed in following
directory: /Library/Java/JavaVirtualMachines
. I have 3 JDK installed in my
machine, so it has 3 separate directory. We can view the content of this
directory using ls
command.
$ ls -la /Library/Java/JavaVirtualMachines
total 0
drwxr-xr-x 5 root wheel 160 May 2 12:48 .
drwxr-xr-x 4 root wheel 128 Jan 23 2020 ..
drwxr-xr-x 3 root wheel 96 May 2 12:48 jdk-16.0.1.jdk
drwxr-xr-x 3 root wheel 96 Dec 13 2016 jdk1.7.0_79.jdk
drwxr-xr-x 3 root wheel 96 Sep 8 2019 jdk1.8.0_221.jdk
Now lets see which version of java
binary is selected by default.
$ java -version
java version "16.0.1" 2021-04-20
Java(TM) SE Runtime Environment (build 16.0.1+9-24)
Java HotSpot(TM) 64-Bit Server VM (build 16.0.1+9-24, mixed mode, sharing)
$ which java
/usr/bin/java
$ readlink $(which java)
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
$ readlink /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
$ ls -la /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
-rwxr-xr-x 1 root wheel 38880 May 28 2020 /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
It looks like java
is alias to
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
.
But this file is not symbolic link. Now lets see which version it is:
$ /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java -version
java version "16.0.1" 2021-04-20
Java(TM) SE Runtime Environment (build 16.0.1+9-24)
Java HotSpot(TM) 64-Bit Server VM (build 16.0.1+9-24, mixed mode, sharing)
By this it seems like it is coping Java 16 binary. But MD5 sum of these two binary doesn’t match.
$ md5 /Library/Java/JavaVirtualMachines/jdk-16.0.1.jdk/Contents/Home/bin/java
MD5 (/Library/Java/JavaVirtualMachines/jdk-16.0.1.jdk/Contents/Home/bin/java) = 6260ad30c7ac6f36226589f8c31d01b9
$ md5 /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
MD5 (/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java) = 41a719da5c04632f527db111d61088d0
Actually binaries within
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands
are stub
applications that determine which Java to use. And by default top Java version
is selected for use.
Introducing java_home
Command
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands
directory has a binary named java_home
which can be used for querying various
installed Java version’s home directory. This binary is aliased as
/usr/libexec/java_home
. As /usr/libexec
directory is not included in $PATH
by
default, so always need to enter full command path.
$ readlink /usr/libexec/java_home
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java_home
Now lets play with java_home
:
# Executing without any args prints top installed Java's Home
$ /usr/libexec/java_home
/Library/Java/JavaVirtualMachines/jdk-16.0.1.jdk/Contents/Home
# -V options shows list of all installed JDK
$ /usr/libexec/java_home -V
Matching Java Virtual Machines (3):
16.0.1, x86_64: "Java SE 16.0.1" /Library/Java/JavaVirtualMachines/jdk-16.0.1.jdk/Contents/Home
1.8.0_221, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home
1.7.0_79, x86_64: "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home
/Library/Java/JavaVirtualMachines/jdk-16.0.1.jdk/Contents/Home
# Can list a specific Java's home with -v
$ /usr/libexec/java_home -v 1.8
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home
# -exec is used to run specific version of java
$ /usr/libexec/java_home -v 1.8 -exec java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
Switching Java Version
Interesting thing is all commands in
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands
obeys
$JAVA_HOME
environment variable. So if we update the JAVA_HOME
variable we
can use different java
version.
# Initial Java Version is 16
$ java -version
java version "16.0.1" 2021-04-20
Java(TM) SE Runtime Environment (build 16.0.1+9-24)
Java HotSpot(TM) 64-Bit Server VM (build 16.0.1+9-24, mixed mode, sharing)
# Exported Java 1.8 as JAVA_HOME
$ export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
# Now its Java 1.8
$ java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
To make it permanent we have write this line in .bash_profile
.
$ echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)' >> ~/.bash_profile
Now we are using system wide Java 8 but we still get jshell
command which was
introduced in Java 9
. But running jshell
shows following error:
$ readlink $(which jshell)
/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jshell
$ jshell
Unable to locate an executable at "/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/bin/jshell" (-1)
But we can write us a simple alias like below:
$ alias jshell16='/usr/libexec/java_home -v 16 -exec jshell'
$ jshell16
| Welcome to JShell -- Version 16.0.1
| For an introduction type: /help intro
jshell>
We can also write alias to easily switch between JDKs like below:
$ alias setJdk7='unset JAVA_HOME; export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)'
$ setJdk7
$ java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)