ChatGPT解决这个技术问题 Extra ChatGPT

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

(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.

Type 'which java' and follow the breadcrumbs. /usr/bin/java is just a symlink
Been there, done that. /usr/bin/java points to /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java. The Versions directory does not contain a symlink to the 1.8.0 JDK. Instead, it contains a directory helpfully called A which Current points to. A is not a "JAVA_HOME. It has a subdirectory called Commands which does have a java command, but it is an opaque universal binary which does who-knows-what. I suspect it uses java_home, etc. to decide which JVM to use.
If this is off-topic, please migrate instead of closing. FWIW, this is about "software tools commonly used by programmers" so closing "off-topic" is disingenuous.
Yes, this is frustrating! I just want one JDK for all, or maybe 2 that I can switch between 1.7 and 1.8 easily.
I found this SO answer useful for this question: stackoverflow.com/a/44169445/2987755

I
Ian Roberts

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.


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.
@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.
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.
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.
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...
v
void256

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.


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/…
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.
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...
I manage to use this answer to avoid using Java 9 launching double-click applications (due to an issue in Keystore Store explorer). Thanks!
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.
U
User404

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!


Unbelievable, but it worked... Thanks Mac OS Mojave
D
Dan Walters

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.


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.
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 &"
A
Andrei Rînea

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.


j
jrypkahauer

It's pretty simple, if you don't mind rolling up your sleeves... /Library/Java/Home is the default for JAVA_HOME, and it's just a link that points to one of:

/System/Library/Java/JavaVirtualMachines/1.?.?.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/jdk1.?.?_??.jdk/Contents/Home

So I wanted to change my default JVM/JDK version without changing the contents of JAVA_HOME... /Library/Java/Home is the standard location for the current JVM/JDK and that's what I wanted to preserve... it seems to me to be the easiest way to change things with the least side effects.

It's actually really simple. In order to change which version of java you see with java -version, all you have to do is some version of this:

cd /Library/Java
sudo rm Home
sudo ln -s /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home ./Home

I haven't taken the time but a very simple shell script that makes use of /usr/libexec/java_home and ln to repoint the above symlink should be stupid easy to create...

Once you've changed where /Library/Java/Home is pointed... you get the correct result:

cerebro:~ magneto$ java -version
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27) Java HotSpot(TM)
64-Bit Server VM (build 25.60-b23, mixed mode)

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.
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.
Well these days I just have this in .bash_profile: export JAVA_HOME=`/usr/libexec/java_home -v 12`
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.
d
duma

Oracle's uninstallation instructions for Java 7 worked for me.

Excerpt:

Uninstalling the JDK To uninstall the JDK, you must have Administrator privileges and execute the remove command either as root or by using the sudo(8) tool. Navigate to /Library/Java/JavaVirtualMachines and remove the directory whose name matches the following format:* /Library/Java/JavaVirtualMachines/jdk...jdk For example, to uninstall 7u6: % rm -rf jdk1.7.0_06.jdk


This question wasn't about un-installation... it was about choosing the "primary" JVM from those one has installed...
M
Mo'in Creemers

A bit late but as this is an ongoing issue with Mac OSX...

The simplest solution I found was to simply remove the OpenJDK stuff that Apple installs. Every time an update of Mac OSX arrives it gets installed and you'll need to remove it again.

This works really well if you develop apps for Google App Engine on your mac using Java. The OpenJDK does not work well and the Java version that comes with the Mac OSX Yosemite upgrade will make the Eclipse Plug-in for App Engine crash on every deployment with the helpful error: "Read timed out".


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.
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.
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.
@whyoz Just installed jdk-8u31-macosx-x64 on osx 10.10.2 and the VM was installed in the JavaVirtualMachines folder as expected.
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..
R
Rajitha Wijayaratne

It's possible to add to java_home very easily.

The way the JAVA_HOME is set on MacOS is as follows. Add the following to your ~/.zshrc for example. The script will pick up the latest JDK installed on your system.

 JAVA_HOME=$(/usr/libexec/java_home)

When the java_home script is run with the -V switch is lists the currently installed JDKs. My MacOS (Monterey) default Java Home (JAVA_HOME) was this.

/usr/libexec/java_home -V (Command to list installed JDK versions) 
        1.8.0_292 (x86_64) "AdoptOpenJDK" - "AdoptOpenJDK 8" /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home

Download the version of Java OpenJDK from here. Make sure to download the JDK .dmg package. This has an installer that updates the JAVA_HOME. After installing v18.0 mine changed to the following.

/usr/libexec/java_home -V
Matching Java Virtual Machines (2):
    18 (x86_64) "Azul Systems, Inc." - "Zulu 18.28.13" /Library/Java/JavaVirtualMachines/zulu-18.jdk/Contents/Home
    1.8.0_292 (x86_64) "AdoptOpenJDK" - "AdoptOpenJDK 8" /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home

Now it picks up the top item i.e. version 18. If the default top item is not preferable, then rejig the names so that the one you want is the first listed as described by void256 above. The sort key is here.

/Library/Java/JavaVirtualMachines/*/Contents/Info.plist.

Now all tools can use the jdk from JAVA_HOME.


Three things: (1) java_home doesn't affect the JAVA_HOME environment variable. (2) java_home always prefers the latest version. I was asking how to set the default version when I don't want the latest. (3) This question was about applications not run from the command-line so environment variables and command-line behavior aren't relevant.
java_home does affect the JAVA_HOME. Add this to your profile init eg. ~/.zshrc -> export JAVA_HOME=$(/usr/libexec/java_home). This will pick it up the top item automatically. If the default top item is not preferable, then rejig the names so that the one you want is the first listed. The sort key is here -> /Library/Java/JavaVirtualMachines/*/Contents/Info.plist.
C
Celandro

Edit: this information is for visualvm specifically, not for any other java app

As mentioned by others, you need to modify the visualvm.conf

For the latest version of JvisualVM 1.3.6 on Mac, the install directories have changed.

It is currently in /Applications/VisualVM.app/Contents/Resources/visualvm/etc/visualvm.conf.

However this may depend on where you have installed VisualVM. The easiest way to find where your VisualVM is to start it, and then look at the process using:

ps -ef | grep VisualVM

You will see something like:

... -Dnetbeans.dirs=/Applications/VisualVM.app/Contents/Resources/visualvm/visualvm...

You want to take the netbeans.dir property and look up a directory and you will find the etc folder.

Uncomment this line in the visualvm.conf and change the path to the jdk

visualvm_jdkhome="/path/to/jdk"

Additionally, if you are having slowness with your visualvm and you have a lot of memory, I would suggest greatly increasing the amount of memory available and running it in server mode:

visualvm_default_options="-J-XX:MaxPermSize=96m -J-Xmx2048m -J-Xms2048m -J-server -J-XX:+UseCompressedOops -J-XX:+UseConcMarkSweepGC -J-XX:+UseParNewGC -J-XX:NewRatio=2 -J-Dnetbeans.accept_license_class=com.sun.tools.visualvm.modules.startup.AcceptLicense -J-Dsun.jvmstat.perdata.syncWaitMs=10000 -J-Dsun.java2d.noddraw=true -J-Dsun.java2d.d3d=false"

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.
Unfortunately as mentioned by others, jvisualvm does not use standard methods for choosing a jvm. This is the only solution for this app.
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.
T
Tony

I had a similar situation, and the following process worked for me:

In the terminal, type vi ~/.profile Then add this line in the file, and save export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk.jdk/Contents/Home where version is the one on your computer, such as 1.7.0_25 Exit the editor, then type the following command make it become effective source ~/.profile

Then type java -version to check the result

    java -version 

What is .profile? From:http://computers.tutsplus.com/tutorials/speed-up-your-terminal-workflow-with-command-aliases-and-profile--mac-30515

.profile file is a hidden file. It is an optional file which tells the system which commands to run when the user whose profile file it is logs in. For example, if my username is bruno and there is a .profile file in /Users/bruno/, all of its contents will be executed during the log-in procedure.


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.
R
Rajat

MacOS uses /usr/libexec/java_home to find the current Java Version. One way to bypass is to change the plist file as explained by @void256 above. Other ways is to take the backup of the java_home and replace it with your own script java_home having the code echo $JAVA_HOME

Now export the JAVA_HOME to the desired version of the SDK by adding the following commands to the ~/.bash_profile. export JAVA_HOME="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home" launchctl setenv JAVA_HOME $JAVA_HOME /// Make the environment variable global

Run the command source ~/.bash_profile to the run the above commands.

Anytime one needs to change the JAVA_HOME he can reset the JAVA_HOME value in the ~/.bash_profile file.


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.
u
user2756423

I wanted to change default java version form 1.6* to 1.7*. I tried the following steps and it worked for me:

Removed link "java" from under /usr/bin

Created it again, pointing to the new location:

ln -s /Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home/bin/java java

verified with "java -version"

java version "1.7.0_51" Java(TM) SE Runtime Environment (build 1.7.0_51-b13) Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)


Does not answer the question: it will not affect the behavior of /usr/libexec/java_home.