How can I change Mac OS's default Java VM returned from /usr/libexec/java_home

137,799

Solution 1

I think JAVA_HOME is the best you can do. The command-line tools like java and javac will respect that environment variable, you can use /usr/libexec/java_home -v '1.7*' to give you a suitable value to put into JAVA_HOME in order to make command line tools use Java 7.

export JAVA_HOME="`/usr/libexec/java_home -v '1.7*'`"

But standard double-clickable application bundles don't use JDKs installed under /Library/Java at all. Old-style .app bundles using Apple's JavaApplicationStub will use Apple Java 6 from /System/Library/Frameworks, and new-style ones built with AppBundler without a bundled JRE will use the "public" JRE in /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home - that's hard-coded in the stub code and can't be changed, and you can't have two different public JREs installed at the same time.


Edit: I've had a look at VisualVM specifically, assuming you're using the "application bundle" version from the download page, and this particular app is not an AppBundler application, instead its main executable is a shell script that calls a number of other shell scripts and reads various configuration files. It defaults to picking the newest JDK from /Library/Java as long as that is 7u10 or later, or uses Java 6 if your Java 7 installation is update 9 or earlier. But unravelling the logic in the shell scripts it looks to me like you can specify a particular JDK using a configuration file.

Create a text file ~/Library/Application Support/VisualVM/1.3.6/etc/visualvm.conf (replace 1.3.6 with whatever version of VisualVM you're using) containing the line

visualvm_jdkhome="`/usr/libexec/java_home -v '1.7*'`"

and this will force it to choose Java 7 instead of 8.

Solution 2

I've been there too and searched everywhere how /usr/libexec/java_home works but I couldn't find any information on how it determines the available Java Virtual Machines it lists.

I've experimented a bit and I think it simply executes a ls /Library/Java/JavaVirtualMachines and then inspects the ./<version>/Contents/Info.plist of all runtimes it finds there.

It then sorts them descending by the key JVMVersion contained in the Info.plist and by default it uses the first entry as its default JVM.

I think the only thing we might do is to change the plist: sudo vi /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Info.plist and then modify the JVMVersion from 1.8.0 to something else that makes it sort it to the bottom instead of the top, like !1.8.0.

Something like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    ...
    <dict>
            ...
            <key>JVMVersion</key>
            <string>!1.8.0</string>   <!-- changed from '1.8.0' to '!1.8.0' -->`

and then it magically disappears from the top of the list:

/usr/libexec/java_home -verbose
Matching Java Virtual Machines (3):
    1.7.0_45, x86_64:   "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home
    1.7.0_09, x86_64:   "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home
    !1.8.0, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home

Now you will need to logout/login and then:

java -version
java version "1.7.0_45"

:-)

Of course I have no idea if something else breaks now or if the 1.8.0-ea version of java still works correctly.

You probably should not do any of this but instead simply deinstall 1.8.0.

However so far this has worked for me.

Solution 3

It's actually pretty easy. Let's say we have this in our JavaVirtualMachines folder:

  • jdk1.7.0_51.jdk
  • jdk1.8.0.jdk

Imagine that 1.8 is our default, then we just add a new folder (for example 'old') and move the default jdk folder to that new folder. Do java -version again et voila, 1.7!

Solution 4

I actually looked at this a little in the disassembler, since source isn't available.

/usr/bin/java and /usr/libexec/java_home both make use of JavaLaunching.framework. The JAVA_HOME environment variable is indeed checked first by /usr/bin/java and friends (but not /usr/libexec/java_home.) The framework uses the JAVA_VERSION and JAVA_ARCH envirionment variables to filter the available JVMs. So, by default:

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (2):
    11.0.5, x86_64: "Amazon Corretto 11"    /Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
    1.8.0_232, x86_64:  "Amazon Corretto 8" /Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home

But setting, say, JAVA_VERSION can override the default:

$ JAVA_VERSION=1.8 /usr/libexec/java_home
/Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home

You can also set JAVA_LAUNCHER_VERBOSE=1 to see some additional debug logging as far as search paths, found JVMs, etc., with both /usr/bin/java and /usr/libexec/java_home.

In the past, JavaLaunching.framework actually used the preferences system (under the com.apple.java.JavaPreferences domain) to set the preferred JVM order, allowing the default JVM to be set with PlistBuddy - but as best as I can tell, that code has been removed in recent versions of macOS. Environment variables seem to be the only way (aside from editing the Info.plist in the JDK bundles themselves.)

Setting default environment variables can of course be done through your .profile or via launchd, if you need them be set at a session level.

Solution 5

I tested "jenv" and other things like setting "JAVA_HOME" without success. Now I ended up with following solution:

function setJava {
    export JAVA_HOME="$(/usr/libexec/java_home -v $1)"
    launchctl setenv JAVA_HOME $JAVA_HOME
    sudo ln -nsf "$(dirname ${JAVA_HOME})/MacOS" /Library/Java/MacOS 
    java -version
}

(added to ~/.bashrc or ~/.bash.profile or ~/.zshrc)

And calling like that:

setJava 1.8

java_home will handle the wrong input. So you can't do something wrong. Maven and other stuff will pick up the right version now.

Share:
137,799
Christopher Schultz
Author by

Christopher Schultz

A Human Apache Software Foundation Member Apache Tomcat Committer (Committer, PMC) Apache Velocity Committer

Updated on August 14, 2021

Comments

  • Christopher Schultz
    Christopher Schultz over 2 years

    (Wasn't sure if this should go on SU... migration is certainly an option, but more programmers read questions here, so here goes).

    I am running Mac OS X 10.8.4, and I have Apple's JDK 1.6.0_51 installed as well as Oracle's JDK 1.7.0_25. I recently installed Oracle's 1.8 preview JDK for some pre-release software that requires it. Now, when I run /usr/libexec/java_home, I get this:

    $ /usr/libexec/java_home -V
    Matching Java Virtual Machines (4):
        1.8.0, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home
        1.7.0_25, x86_64:   "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home
        1.6.0_51-b11-457, x86_64:   "Java SE 6" /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
        1.6.0_51-b11-457, i386: "Java SE 6" /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
    

    Great.

    However, running:

    $ java -version
    

    Returns:

    java version "1.8.0-ea"
    

    That means that the default version of Java is currently the pre-release version, which breaks some "normal" packages (in my case, VisualVM).

    I can't set JAVA_HOME because launching applications ignores environment variables, even when launching from the command line (e.g. $ open /Applications/VisualVM.app).

    So, is there a file I can edit where I can set my JVM ordering preferences globally?

    (Please don't tell me to launch the Java Preferences Panel because that simply does not work: it does not contain anything useful and only lists one of the 4 JVMs that I have installed.)

    Update:

    Oracle JVMs live in /Library/Java/JavaVirtualMachines. Re-naming the JDK 1.8 directory to jdk1.8.0.jvm.xyz does not change anything: java_home still finds it in the right place, and running /usr/bin/java still executes the 1.8 JVM. This is not an issue with synlinks, etc.

    Answers to Similar Questions

    While this answer offers what amounts to a hack that will remove versions of Java from being picked up by java_home, it still does not answer this question of how java_home chooses its default and whether or not users can non-destructively set it.

  • Christopher Schultz
    Christopher Schultz almost 11 years
    This does not appear to be the case on my system. Launching VisualVM before JDK 1.8 was installed worked. After JDK1.8, VisualVM shows the splash screen, then dies. Moving the JDK1.8 directory out of /Library/Java restores its ability to run.
  • Ian Roberts
    Ian Roberts almost 11 years
    @ChristopherSchultz I've had a look inside the VisualVM bundle and it turns out it's not a normal appbundler application. See my edit for a possible workaround.
  • Christopher Schultz
    Christopher Schultz almost 11 years
    Sorry, I wrote my previous comment before your edit. I'll check-out getting VisualVM to run using that technique, but it's unlikely to be universally-applicable. I have a bunch of other Java-based software tat I run as well like Eclipse, JasperReports iReport, etc. that are all likely to be affected by this. I think I'd rather just move the JDK1.8 directory somewhere else and use that explicitly with JAVA_HOME for the (few) times that I actually need it.
  • Christopher Schultz
    Christopher Schultz over 10 years
    This question wasn't about un-installation... it was about choosing the "primary" JVM from those one has installed...
  • Christopher Schultz
    Christopher Schultz over 10 years
    Bad advice: modifying the startup script for a particular application will likely break the application and does not solve the original problem of changing the default JVM for the OS.
  • Celandro
    Celandro over 10 years
    Unfortunately as mentioned by others, jvisualvm does not use standard methods for choosing a jvm. This is the only solution for this app.
  • Ian Roberts
    Ian Roberts over 10 years
    As I state in my answer you don't need to modify anything inside the application bundle itself, the app can load its configuration from ~/Library/Application Support/VisualVM/1.3.6/etc/visualvm.conf.
  • andrewdotn
    andrewdotn over 10 years
    Yup, you’re right, JAVA_HOME is the way to go and in general your best bet is to specify the minor version you need in other cases. Based on disassembly, it turns out you can export JAVA_VERSION=1.7 to make java_home default to showing JKD7 instead of JDK8, but that breaks java_home -v 1.6 because java-home interprets it as an additional constraint and gives up due to mutually unsatisfiable constraints, then just goes with the default 1.8 even with the --failfast option.
  • antoine
    antoine over 10 years
    This worked for me. I have had to use this tweak to keep the Idea Sbt plugin working for me on MacOS. I am mentioning it on my blog agilebuild.blogspot.com/2014/02/…
  • Christopher Schultz
    Christopher Schultz about 10 years
    This will not work when launching VisualVM from Launchpad. Launching from the command-line is never a problem, as you can set the JAVA_HOME environment variable.
  • Christopher Schultz
    Christopher Schultz about 10 years
    Does not answer the question: it will not affect the behavior of /usr/libexec/java_home.
  • Christopher Schultz
    Christopher Schultz almost 10 years
    Anything that relies on environment variables will not work. The point is that applications launched via LaunchPad, etc. won't have that environmental setup. The plist hack above seems like the "best" one in that it actually achieves the desired result. I'm not sure about any downsides, yet. See the answer from @Tony which has the same problem.
  • JGFMK
    JGFMK over 9 years
    I can't understand why the System Preferences Java Control Panel doesn't just present a list to select from, rather than have to resort to shell scripts/commands. I suspect this is just for Applets that run in the browser...
  • Weibo Li
    Weibo Li over 9 years
    This works but it seems a little tricky and might not look like a standard operation procedure. I wander if there is a better approach.
  • Christopher Schultz
    Christopher Schultz over 9 years
    Funny... I thought Apple had removed Java altogether at this point. I don't remember manually removing Apple's Java 1.6 JVM, and it's definitely no longer here. At any rate, this doesn't really fix the original problem which was to specify the preferred JVM given a selection that have been installed.
  • Mo'in Creemers
    Mo'in Creemers over 9 years
    You are correct. It doesn't answer the question. It does answer this: If you remove the JVM being used, the 'next' one in the list is used. Maybe that helps.
  • David Resnick
    David Resnick over 9 years
    I would still like a solution for this, but for setting the JDK for Intellij to use I added this to my zshenv: export IDEA_JDK=/usr/libexec/java_home -v 1.7. I think I'll do the same for JAVA_HOME...
  • Jack
    Jack over 9 years
    @IanRoberts Hi, I have similar issue in bundling my Java application, I would be grateful if you could have a look at my question as well, thanks stackoverflow.com/questions/28207295/…
  • whyoz
    whyoz over 9 years
    does this explain why after running a jdk 8 install, it doesn't show up in the JavaVirtualMachines folder? All I see is "1.6.0.jdk" no matter what version I install.
  • Mo'in Creemers
    Mo'in Creemers over 9 years
    @whyoz Just installed jdk-8u31-macosx-x64 on osx 10.10.2 and the VM was installed in the JavaVirtualMachines folder as expected.
  • whyoz
    whyoz over 9 years
    Are you running Parallels by chance? I installed it on the Windows side of Parallels and 8u31 installed as expected..just not on the Mac side..
  • Christopher Schultz
    Christopher Schultz over 8 years
    This isn't how this stuff works: /Library/Java/Home is indeed a symlink, but it points to /System/Library/Frameworks/JavaVM.framework/Home which itself is in a big jumble of symlinks that finally gets you to ... a magical command that determines the right JRE to launch. Note that /usr/libexec/java_home also links into this magic. So, you can interrupt everything by just replacing symlinks and pointing to a single JRE, but you'll have to update that every time. There is evidently no command like set_preferred_jvm_version or something similar.
  • Christopher Schultz
    Christopher Schultz over 8 years
    The advantage of this technique, though, is that it does not require you to set JAVA_HOME anywhere. I'll play with this technique to see if it will result in Java-based programs launching with the "preferred" Java VM. I suspect it will, but it's pretty fragile.
  • Nicolas Henneaux
    Nicolas Henneaux over 6 years
    I manage to use this answer to avoid using Java 9 launching double-click applications (due to an issue in Keystore Store explorer). Thanks!
  • Jeremy Kao
    Jeremy Kao almost 6 years
    An excerpt from Installation of the JDK and the JRE on macOS: After installing Java for macOS 2012-006, /usr/bin/java will find the newest JDK installed, and will use that for all of the Java-related command-line tools in /usr/bin.
  • Mike Ryan
    Mike Ryan over 5 years
    Well, you're on to something! Of course, it'd be great to know if there is a way to do this in a way that the designers of the java_home executable intended. You know, like real documentation rather than the incredibly terse output of java_home -help. Sadly, it looks like java_home simply doesn't offer this functionality.
  • Michał Dobi Dobrzański
    Michał Dobi Dobrzański almost 5 years
    Unbelievable, but it worked... Thanks Mac OS Mojave
  • Christopher Schultz
    Christopher Schultz over 4 years
    This is great information, Dan. Using .profile isn't useful for my use-case (launching an application from e.g. launchpad) but the tip on launchd is a good one. I'll have to give that a try, since Java's recent versioning madness means that I have several generations of Java installed simultaneously, with varying levels of (personal) trust.
  • jrypkahauer
    jrypkahauer over 4 years
    Well these days I just have this in .bash_profile: export JAVA_HOME=`/usr/libexec/java_home -v 12`
  • Christopher Schultz
    Christopher Schultz over 4 years
    This doesn't work for double-clicking on an icon, which was kind of the whole point. Solutions which only work from the command-line are ... not solutions.
  • Greg Fenton
    Greg Fenton over 3 years
    This is great! I am running a slightly convoluted Eclipse setup and have several JDKs installed. So I just created a different BASH script for each Eclipse config I need that simply has the line: "JAVA_VERSION=1.8 /Applications/Eclipse.app/Contents/MacOS/eclipse &"
  • bompf
    bompf about 2 years
    It seems like the tool also checks ~/Library/Java/JavaVirtualMachines for the currently logged in user.