如何更改Mac OS中默认的Java虚拟机,该虚拟机位于/usr/libexec/java_home?

136

我不确定这个问题是否应该放在SU上,迁移肯定是一个选择,但更多的程序员在这里阅读问题,所以就在这里问了。

我正在运行Mac OS X 10.8.4,并安装了苹果的JDK 1.6.0_51和Oracle的JDK 1.7.0_25。最近,我安装了Oracle的1.8预览版JDK,因为一些需要它的预发布软件。现在,当我运行/usr/libexec/java_home时,我得到了这个:

$ /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

很好。

然而,运行:

$ java -version

返回:
java version "1.8.0-ea"

这意味着默认版本的Java目前是预发布版本,这会破坏一些“正常”的软件包(在我的情况下是VisualVM)。
我无法设置JAVA_HOME,因为启动应用程序会忽略环境变量,即使从命令行启动也是如此(例如$ open /Applications/VisualVM.app)。
那么,有没有一个文件我可以编辑,在其中可以全局设置我的JVM排序偏好?
(请不要告诉我启动Java首选项面板,因为那根本行不通:它不包含任何有用的内容,只列出我安装的4个JVM中的一个。)
更新:
Oracle JVM位于/Library/Java/JavaVirtualMachines。将JDK 1.8目录重命名为jdk1.8.0.jvm.xyz并不会改变任何东西:java_home仍然在正确的位置找到它,并且运行/usr/bin/java仍然执行1.8 JVM。这不是符号链接等问题。
类似问题的答案:

虽然这个答案提供了一种所谓的“hack”,可以阻止 java_home 选择 Java 版本,但它仍然没有回答这个问题:java_home 如何选择其默认值,以及用户是否能够非破坏性地设置它


输入“which java”并跟随面包屑。 “/usr/bin/java”只是一个符号链接。 - Brian Roach
13
已经有过这种经历。/usr/bin/java指向/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/javaVersions目录中不包含指向1.8.0 JDK的符号链接。相反,它包含一个很有用的名为A的目录,Current指向它。A不是一个"JAVA_HOME"。它有一个名为Commands的子目录,其中包含一个java命令,但它是一个不透明的通用二进制文件,做了什么无从得知。我怀疑它使用java_home等来决定要使用哪个JVM。 - Christopher Schultz
2
如果这个问题不适合,请迁移而不是关闭。顺便说一句,这是关于“程序员常用的软件工具”,关闭“不适合”的做法是不诚实的。 - Christopher Schultz
是的,这很令人沮丧!我只想要一个适用于所有的JDK,或者也许有两个版本,我可以轻松地在1.7和1.8之间切换。 - Brian
1
我发现这个stackoverflow答案对这个问题很有用:https://dev59.com/7mEh5IYBdhLWcg3w1mbs#44169445 - dkb
15个回答

98

我认为 JAVA_HOME 是最好的选择。像javajavac这样的命令行工具将遵守该环境变量,你可以使用/usr/libexec/java_home -v '1.7*' 来获取一个适合放入JAVA_HOME中的值,以便让命令行工具使用Java 7。

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

标准的双击应用程序包根本不使用安装在 /Library/Java 下的 JDK。使用 Apple 的 JavaApplicationStub 的旧式 .app 包将使用从 /System/Library/Frameworks 获得的 Apple Java 6,而使用AppBundler构建的没有捆绑 JRE 的新样式则会使用位于 /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home 的“公共”JRE —— 这是硬编码在存根代码中的,无法更改,并且您不能同时安装两个不同的公共 JRE。


编辑:我查看了特定的 VisualVM,假定您正在使用来自下载页面的“应用程序包”版本,这个特殊的应用程序不是 AppBundler 应用程序,而是一个调用许多其他 shell 脚本并读取各种配置文件的 shell 脚本。它默认选择来自 /Library/Java 的最新 JDK,只要那是 7u10 或更高版本,或者如果您的 Java 7 安装是更新 9 或更早版本,则使用 Java 6。但是,解开 shell 脚本中的逻辑后,我认为您可以使用配置文件指定特定的 JDK。

创建一个文本文件 ~/Library/Application Support/VisualVM/1.3.6/etc/visualvm.conf(将 1.3.6 替换为您使用的 VisualVM 版本),其中包含行

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

这将强制选择Java 7而不是8。


在我的系统上似乎不是这种情况。 在安装JDK1.8之前启动VisualVM有效。 在安装JDK1.8后,VisualVM显示启动画面,然后就消失了。 将JDK1.8目录移出/Library/Java可以恢复其运行能力。 - Christopher Schultz
@ChristopherSchultz 我已经查看了VisualVM捆绑包的内部内容,发现它不是一个普通的appbundler应用程序。请参见我的编辑,以获取可能的解决方法。 - Ian Roberts
抱歉,在您编辑之前我已经写了我的上一个评论。我会尝试使用那种技术来运行VisualVM,但这不太可能是普适的。我还有一堆其他基于Java的软件,比如Eclipse、JasperReports iReport等,它们都很可能受到影响。我想我更愿意将JDK1.8目录移动到其他地方,并在实际需要时明确使用JAVA_HOME。 - Christopher Schultz
1
没错,JAVA_HOME 是最好的选择,通常最好指定你需要的次要版本。根据反汇编,结果表明你可以 export JAVA_VERSION=1.7 来使 java_home 默认显示 JKD7 而不是 JDK8,但这会破坏 java_home -v 1.6,因为 java-home 将其解释为附加约束条件并放弃相互不可满足的约束条件,然后即使使用 --failfast 选项也仍将默认为 1.8。 - andrewdotn
2
我不明白为什么系统偏好设置中的Java控制面板不能只提供一个选择列表,而非必须使用shell脚本/命令。我猜想这只是针对在浏览器中运行的小程序... - JGFMK
@IanRoberts 你好,我在打包我的Java应用程序时遇到了类似问题,如果您能看一下我的问题,我将不胜感激,谢谢。http://stackoverflow.com/questions/28207295/failed-to-bundle-java-application-1-8-for-mac - Jack

64

我曾经也遇到过同样的问题,无论我去哪里搜索都找不到关于 /usr/libexec/java_home 如何工作的信息,它是如何确定可用的Java虚拟机列表的。

我进行了一些实验,我认为它只是执行 ls /Library/Java/JavaVirtualMachines,然后检查它发现的所有运行时的 ./<version>/Contents/Info.plist

然后,它通过 Info.plist 中包含的键 JVMVersion降序排序,默认情况下使用第一个条目作为其默认JVM。

我想我们唯一能做的事情就是更改 plist:sudo vi /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Info.plist,然后将 JVMVersion 从 1.8.0 修改为其他值,使其按底部而非顶部排序,比如 !1.8.0

类似于:

<?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' -->`

然后它神奇地从列表顶部消失了:

/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

现在您需要注销/登录,然后执行以下操作:

java -version
java version "1.7.0_45"

当然,我不知道现在是否还有其他问题,或者java的1.8.0-ea版本是否仍然正常工作。

你可能不应该这样做,而是简单地卸载1.8.0版本。

但是到目前为止,这对我有效。


这对我有用。我不得不使用这个小技巧来让我的MacOS上的Idea Sbt插件正常工作。我在我的博客http://agilebuild.blogspot.com/2014/02/using-intellij-sbt-plugin-and-fighting.html中提到了它。 - antoine
3
在 macOS 上安装 JDK 和 JRE 中,一段摘录如下:安装完“Java for macOS 2012-006”后,/usr/bin/java 将会找到最新的已安装JDK,并且将用它来操作/usr/bin中所有与Java相关的命令行工具。 - Jeremy Kao
很好,你已经有了一些想法!当然,如果能知道以java_home可执行文件设计者预期的方式来完成这个任务,那就太好了。你知道,像真正的文档一样,而不是java_home -help输出的极其简洁的内容。遗憾的是,看起来java_home根本没有提供这种功能。 - Mike Ryan
工具似乎还会检查当前登录用户的 ~/Library/Java/JavaVirtualMachines 目录。 - bompf
除了修改“Info.plist”的内容之外,另一个选项是为您想要排除的版本简单地重命名文件:mv Info.plist Info.plist.disabled(可能需要使用sudo)。撤消此操作就像将文件重新命名一样容易。 - friederbluemle
显示剩余3条评论

14

源代码不可用,我在反汇编器中看了一下这个问题。

/usr/bin/java 和 /usr/libexec/java_home 都使用了 JavaLaunching.framework。/usr/bin/java 及其相关程序确实首先检查 JAVA_HOME 环境变量(但不包括 /usr/libexec/java_home)。该框架使用 JAVA_VERSION 和 JAVA_ARCH 环境变量来过滤可用的 JVM。默认情况下:

$ /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

但是,例如设置JAVA_VERSION可以覆盖默认值:

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

您还可以设置JAVA_LAUNCHER_VERBOSE=1,以查看有关搜索路径、找到的JVM等方面的一些额外的调试记录,同时使用/usr/bin/java和/usr/libexec/java_home。

过去,JavaLaunching.framework实际上使用首选项系统(在com.apple.java.JavaPreferences域下)设置首选的JVM顺序,允许使用PlistBuddy设置默认JVM - 但据我所知,该代码已在最近的macOS版本中删除。除了编辑JDK捆绑包自身的Info.plist之外,环境变量似乎是唯一的方法。

当然,可以通过.profile或通过launchd设置默认环境变量,如果您需要在会话级别设置它们。


1
这是非常有用的信息,丹。对于我的用例(例如从启动台启动应用程序),使用.profile并不实用,但是关于launchd的提示很好。我将尝试一下,因为Java最近的版本混乱意味着我同时安装了几代Java,具有不同程度的(个人)信任。 - Christopher Schultz
太好了!我正在运行一个稍微复杂的Eclipse设置,并安装了几个JDK。因此,我为每个我需要的Eclipse配置创建了不同的BASH脚本,其中只有一行:“JAVA_VERSION=1.8 /Applications/Eclipse.app/Contents/MacOS/eclipse&”。 - Greg Fenton
哇塞,这是最好的答案,应该排在第一位。谢谢!所以,在了解我们可以使用JAVA_VERSION更改java_home返回的默认JVM之后,我所做的就是使用我的首选JVM“launchctl setenv JAVA_VERSION”,然后您只需使用“launchtl setenv JAVA_HOME =/usr/libexec/java_home”。然后JAVA_VERSION和JAVA_HOME将在任何地方都可用。您始终可以更新JAVA_VERSION并重新执行脚本以立即在所有位置更新它(显然需要重新启动使用它的任何应用程序)。 - Ignacio Baca
这个答案很棒!肯定应该成为最佳答案。设置 JAVA_HOME 绝对是最简单和最准确的方法。谢谢! - Osama Nabih

10

这其实非常简单。 假设我们在JavaVirtualMachines文件夹中有以下内容:

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

假设1.8是我们的默认版本,那么我们只需添加一个新文件夹(例如“old”),然后将默认的jdk文件夹移动到该新文件夹中。 再次运行java -version,就会出现1.7了!


1
难以置信,但它确实起作用了... 感谢 Mac OS Mojave。 - Michał Dobi Dobrzański

7

我尝试了"jenv"和设置"JAVA_HOME"等其他方法,但都没有成功。现在我找到了以下解决方案:

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
}

(添加到~/.bashrc或~/.bash.profile或~/.zshrc文件中)

然后像这样调用:

setJava 1.8

java_home会处理错误的输入。因此您不能犯错误。 现在,Maven和其他东西将选择正确的版本。


嗯...我得看看 launchctl setenv。这可能真的可以完成工作。 - Christopher Schultz
zsh: 找不到命令:setJava - Maveňツ

5

如果您不介意动手的话,这很简单... /Library/Java/Home 是JAVA_HOME的默认值,它只是一个指向以下路径之一的链接:

  • /System/Library/Java/JavaVirtualMachines/1.?.?.jdk/Contents/Home
  • /Library/Java/JavaVirtualMachines/jdk1.?.?_??.jdk/Contents/Home

所以我想改变我的默认JVM/JDK版本而不需要更改JAVA_HOME的内容... /Library/Java/Home是当前JVM/JDK的标准位置,我想保留它... 在我看来,这似乎是最容易带来最少副作用的方法。

实际上非常简单。为了更改使用java -version命令时显示的Java版本,您只需要执行以下某个版本的命令:

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

我没有花时间编写一个非常简单的脚本,它利用/usr/libexec/java_home和ln来重新定向上述符号链接应该很容易创建...

一旦你改变了/Library/Java/Home的指向... 你会得到正确的结果:

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)

1
这不是这些东西的工作方式:/Library/Java/Home确实是一个符号链接,但它指向/System/Library/Frameworks/JavaVM.framework/Home,后者本身就在一堆符号链接中,最终可以让您找到...一个神奇的命令来确定正确的JRE启动。请注意,/usr/libexec/java_home也链接到此魔法。因此,您可以通过替换符号链接并指向单个JRE来中断所有内容,但每次都必须更新。显然没有像set_preferred_jvm_version或类似的命令。 - Christopher Schultz
1
这种技术的优点是它不需要在任何地方设置 JAVA_HOME。我将运用这种技术来看看是否能使基于Java的程序使用“首选”Java虚拟机启动。我认为这样做会成功,但它非常脆弱。 - Christopher Schultz
1
现在我只是在.bash_profile中加入了这个:export JAVA_HOME=`/usr/libexec/java_home -v 12` - jrypkahauer
这对于双击图标无效,而这基本上是整个重点所在。仅在命令行中有效的解决方案不是解决方案。 - Christopher Schultz

3
有点晚了,但由于这是Mac OSX的持续问题...我发现最简单的解决方法是删除苹果安装的OpenJDK。每次Mac OSX更新时,它都会被安装,你需要再次将其删除。
如果你在Mac上使用Java为Google App Engine开发应用程序,那么这个方法非常有效。OpenJDK不能正常工作,而Mac OSX Yosemite升级附带的Java版本会导致App Engine的Eclipse插件在每次部署时崩溃,并显示"读取超时"错误。

1
有趣的是...我以为苹果已经完全删除了Java。我不记得手动删除过苹果的Java 1.6 JVM,而且它肯定不在这里了。无论如何,这并没有真正解决最初的问题,即指定已安装的选择中首选的JVM。 - Christopher Schultz
你说得对。它并没有回答问题。但是它回答了这个问题:如果你移除正在使用的JVM,那么列表中的“下一个”将被使用。也许这会有所帮助。 - Mo'in Creemers
这是否解释了为什么在运行jdk 8安装程序后,它不会出现在JavaVirtualMachines文件夹中?无论我安装哪个版本,我只能看到“1.6.0.jdk”。 - whyoz
@whyoz 刚刚在 osx 10.10.2 上安装了 jdk-8u31-macosx-x64,VM 正如预期地安装在 JavaVirtualMachines 文件夹中。 - Mo'in Creemers
你是偶然在运行 Parallels 吗?我在 Parallels 的 Windows 系统上安装了 8u31,如预期一样安装成功了...但在 Mac 系统上没有安装成功。 - whyoz

3

Oracle的Java 7卸载说明适用于我的情况。

以下是摘录:

卸载JDK 要卸载JDK,您必须拥有管理员权限,并作为root用户或使用sudo(8)工具执行remove命令。

导航到/Library/Java/JavaVirtualMachines并删除名称与以下格式匹配的目录:*

/Library/Java/JavaVirtualMachines/jdk<major>.<minor>.<macro[_update]>.jdk

例如,要卸载7u6:

% rm -rf jdk1.7.0_06.jdk


2
这个问题不是关于卸载的...而是关于从已安装的JVM中选择“主要”JVM的问题... - Christopher Schultz

-1

命令 /usr/libexec/java_home 可以搜索新的 Java 版本。 例如,JD_GUI.app。在这个应用程序中,我无法运行。所以我在 /Applications/JD-GUI.app/Contents/MacOS/universalJavaApplicationStub.sh 中找到一些脚本来搜索 JAVA_HOME,我只得到直接路径。然后可以运行。 在此输入图像描述

在此输入图像描述 然后看起来像这样。 在此输入图像描述 只是娱乐。


1
目前来看,你的回答不够清晰。请编辑以添加更多细节,以帮助其他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - undefined

-1

编辑:此信息仅适用于VisualVM,不适用于其他Java应用程序

正如其他人所提到的,您需要修改visualvm.conf文件

对于Mac上最新版本的JvisualVM 1.3.6,安装目录已更改。

它目前位于/Applications/VisualVM.app/Contents/Resources/visualvm/etc/visualvm.conf

但是这可能取决于您安装VisualVM的位置。找到VisualVM的最简单方法是启动它,然后使用以下命令查看进程:

ps -ef | grep VisualVM

您将看到类似以下内容的东西:

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

您需要获取netbeans.dir属性并查找一个目录,您将找到etc文件夹。

取消visualvm.conf中的此行注释,并将路径更改为jdk的路径

visualvm_jdkhome="/path/to/jdk"

此外,如果您在使用visualvm时遇到速度缓慢的问题,并且拥有大量内存,我建议大幅增加可用内存并以服务器模式运行:
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"

错误的建议:修改特定应用程序的启动脚本可能会破坏该应用程序 并且 不能解决更改 OS 默认 JVM 的根本问题。 - Christopher Schultz
不幸的是,正如其他人所提到的,jvisualvm没有使用标准方法来选择jvm。这是该应用程序的唯一解决方案。 - Celandro
正如我在我的回答中所述,您不需要修改应用程序包本身内部的任何内容,该应用程序可以从~/Library/Application Support/VisualVM/1.3.6/etc/visualvm.conf加载其配置。 - Ian Roberts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接