java_home and JAVA_HOME on macOS

Ever wondered where Java stuff lives on your Apple Mac? Ever had to find how to set your environment to work with a specific version of Java? Ever manually set the JAVA_HOME environment variable to get something working?

If so, this note may help.

macOS is the operating system that runs on Apple Mac hardware. I have used Java on these machines for many years. A lot of things have changed in that time: Apple used to bundle Java; Oracle bought Sun; Apple stopped bundling Java … macOS was previously known as Mac OS X and later OS X. Oracle still use the old OS X branding.

I have just installed the latest Java upgrade and decided to try to understand how to work with the different versions of Java on macOS. Then I decide to write about it!

I read JDK 8 Installation for OS X from the Java Platform SE Installation Guide some time ago. But I failed to appreciate the java_home command.

This Note for Geeks:

  • describes how to use java_home to set the JAVA_HOME environment variable
  • describes how the java_home --exec <command> can be used to execute Java commands for a specific JDK version independent of JAVA_HOME
  • describes a bash profile to enable interactive setting of the JDK version
  • shares some observations on Java symbolic links on macOS
  • is relevant to Java on macOS and versions of OS X since 2012

See Oracle OS X Platform Install FAQ for further information.

ORACLE — IMPORTANT INFORMATION REGARDING THE SECURITY OF JAVA SE

Earlier versions of Java SE have serious security risks corrected in later versions.
That creates a serious security vulnerability. Even if you installed the most recent version of Java SE, the personal information on your computer may be at risk because earlier, less secure versions could still be executed.
To fix this problem, visit http://java.com/uninstall, where instructions on how to uninstall older versions of Java SE are provided.

java_home

/usr/libexec/java_home is the simplest maintainable way of setting JAVA_HOME on macOS.

You can use java_home to:

  • Find all installed JDKs
  • Get value for JAVA_HOME for a specific JDK version
  • Get the value of JAVA_HOME for the default JDK version
  • Execute specific versions of Java commands

Finding all installed JDKs

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (4):
1.8.0_121, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home
1.7.0_45, x86_64: "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home
1.6.0_65-b14-468, x86_64: "Java SE 6" /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
1.6.0_65-b14-468, i386: "Java SE 6" /Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
NOTE: I will consider uninstalling the 1.6 versions to remove any associate security vulnerabilities

Getting values for JAVA_HOME for specific JDK versions

$ /usr/libexec/java_home -v 1.6 
/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
$ /usr/libexec/java_home -v 1.7
/Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home
$ /usr/libexec/java_home -v 1.8
/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home

Getting the value of JAVA_HOME for the default JDK version

$ /usr/libexec/java_home
/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home

Executing specific versions of Java commands, such as java

java_home is the best way on macOS to execute a Java command ...jdk/Contents/Home/bin/<command> for a specific version of the JDK. And it is independent of the setting of JAVA_HOME.

  • Executing the default version of a java <command>
/usr/libexec/java_home --exec <command>

For example:

$ /usr/libexec/java_home --exec java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
  • Executing a specific version of java command
$ /usr/libexec/java_home -v 1.6 --exec java

For example:

$ /usr/libexec/java_home -v 1.6 --exec java -version
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-468-11M4833)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-468, mixed mode)

man java_home

java_home(1)                                                                                                                                                                              
NAME
java_home - return a value for $JAVA_HOME

-v or --version version
Filters the returned JVMs by the major platform version in "JVMVersion" form.
Example versions: "1.5+", or "1.6*".

-V or --verbose
Prints the matching list of JVMs and architectures to stderr.

--exec command ...
Executes the command at $JAVA_HOME/bin/<command> and passes the remaining arguments.
Any arguments to select which $JAVA_HOME to use must precede the --exec option.
NOTE: The use of $JAVA_HOME in the man page is misleading because:
java_home can be used to return a value that can be used to set $JAVA_HOME
The --exec command can be used to execute a command .../Home/bin/<command> as if it was $JAVA_HOME/bin/<command> without changing or using $JAVA_HOME.

JAVA_HOME

The JAVA_HOME environment variable contains the home location of an installed JDK.

/usr/libexec/java_home is the simplest maintainable way of setting JAVA_HOME.

  • Set JAVA_HOME to JDK version 1.7
export JAVA_HOME=`/usr/libexec/java_home -v 1.7`
  • Set JAVA_HOME to the default JDK
export JAVA_HOME=`/usr/libexec/java_home`
  • Check JAVA_HOME
$ echo $JAVA_HOME 
/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home
  • Check the actual java version
$ java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Independence of java_home --exec from $JAVA_HOME

java_home --exec <command> can be used to execute Java commands independent of the setting of $JAVA_HOME.

The following examples demonstrate executing three different versions of the java command without changing $JAVA_HOME:

  • version using $JAVA_HOME
$ echo $JAVA_HOME 
/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
$ java -version
java version "1.6.0_65"
  • specific version using java_home --exec java -v 1.7
$ /usr/libexec/java_home -v 1.7 --exec java -version
java version "1.7.0_45"
  • default version using java_home --exec java
$ /usr/libexec/java_home --exec java -version
java version "1.8.0_121"

How I use java_home to set JAVA_HOME

This setup is for bash, the Bourne Again Shell on macOS. There are similar variations for other shells.

Set environment variables in .bash_profile

# aliase commands to enable easy setting of JDK version
alias setJDK6='export JAVA_HOME=`/usr/libexec/java_home -v 1.6`'
alias setJDK7='export JAVA_HOME=`/usr/libexec/java_home -v 1.7`'
alias setJDK8='export JAVA_HOME=`/usr/libexec/java_home -v 1.8`'

# set to the default JDK
export JAVA_HOME=`/usr/libexec/java_home`

This profile creates aliases to enable interactive setting of the JDK version and sets JAVA_HOME to the default JDK.

Use the aliases to interactively set JAVA_HOME

Set JAVA_HOME to v 1.7

$ setJDK7

Check $JAVA_HOME

$ echo $JAVA_HOME 
/Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home

Check java version

$ java -version
java version "1.7.0_45"

Set JAVA_HOME to v 1.8

$ setJDK8

Check $JAVA_HOME

$ echo $JAVA_HOME 
/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home

Check java version

$ java -version
java version "1.8.0_121"

I got the idea for the aliases from https://wimdeblauwe.wordpress.com/2014/03/20/switching-easily-between-java-jdks-on-mac-os-x/.

Java symbolic links on macOS

I have been lost a few times trying to follow the symbolic linking of Java versions in and out of /System/Library/Frameworks/JavaVM.framework/Versions/ and /Library/Java/JavaVirtualMachines/.

The following are what seem to matter.

Which java

$ which java
/usr/bin/java

java symlink

The java command is symlinked deep in the JavaVM.framework

$ ls -l /usr/bin/java
/usr/bin/java -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java

java_home symlink

The java_home command is symlinked deep in the JavaVM.framework

$ ls -l /usr/libexec/java_home
/usr/libexec/java_home -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java_home

Thanks to https://superuser.com/questions/879601/how-to-recover-usr-libexec-java-home-executable-on-mac-os-x-10-8-5

Assumption

The Java commands in /usr/bin/ appear to execute the corresponding Java commands for JAVA_HOME.

Each Java command /usr/bin/<command> executes $JAVA_HOME/bin/<command>

For example:

/usr/bin/java executes $JAVA_HOME/bin/java