自上一个技巧成功以来已经过去了一年:在YouTube上发布视频而不是发布。 在文字版本中,
“关于单例的可耻讨论”在YouTube上获得了7,000的观看次数,在Habré上获得了两倍的观看次数。 对于完全顽固的状态写的文章,并讲述古老的纽扣手风琴-这是成功的一点。
今天,我整夜都在安装新版本。 这次的话题是新近出现的:致力于实验技术的历史-SubstrateVM。 但是坚韧程度已经上升到一个新的水平。
真的很期待您的评论! 我提醒您,如果您真的想改善本文中的内容,最好将其
提交到Github上 。 我想说“喜欢并订阅
新频道 ,但是所有发行版本仍会在您的Java集线器中吗?”
从技术上讲:视频在结尾处有一层胶合。 我刚刚写了一个未压缩的视频,而我仅有500 GB的m2 ssd迅速溢出。 而且没有其他硬盘驱动器可以承受如此大的数据压力。 因此,我不得不断开半个小时的连接,然后又设法找到另外五十个演出来记录最后几分钟。 这是通过删除
GoogleChrome编译的文件来实现的。 我在录制时就在FB中写了关于录制软件的文章,这有很多痛苦。
技术上的另一个有趣之处:YouTube由于某种原因阻止了我进行实时流式传输。 同时,帐户上没有任何罢工和污名。 希望这只是一个门框,在90天后一切都会退还。
本文将引用Oracle拥有的代码。 您不能自己使用此代码(除非您阅读了原始许可证,并且在例如GPL的条件下允许使用它)。 这不是在开玩笑。 奥尔索,我警告过。
普里卡兹卡(童话故事将会出现)
许多人已经听说过“新的Java将用Java编写”的故事,并想知道这将是怎么回事。 有一份
《大都市计划》政策文件和
John Rose的相应信函,但那里的一切都很模糊。
这听起来像是一种令人毛骨悚然的血腥魔法。 您可以立即尝试进行同样的操作,不仅没有魔法,而且当您用铁锹敲牙时,一切都像铲子的背面一样愚蠢。 当然,有一些细微差别,但这将是在很晚以后的某一天。
我将以一个发生在夏天的启发性故事为例进行演示。 他们如何在学校撰写论文“我如何度过了夏天”。
首先,我要说一点。 当前在Oracle Labs中进行提前编译的项目是GraalVM。 实际上,进行nishtyaki并将Java代码转换为可执行文件(转换为可执行文件)的组件是SubstrateVM或SVM。 请勿将其与数据讽刺专家(支持向量机)使用的缩写混淆。 关于SVM,作为关键部分,我们将进一步讨论。
问题陈述
因此,“我如何度过夏天”。 我坐在度假中,在
Grail github上花了两个F5,碰到
了一个 :

一个人希望
os.version
提供正确的值。
好吧,我想修复该错误? 男孩说-男孩做了。
我们将检查客户是否在撒谎。
public class Main { public static void main(String[] args) { System.out.println(System.getProperty("os.version")); } }
首先,实际Java中的排气效果是:
4.15.0-32-generic
。 是的,这是新鲜的Ubuntu LTS Bionic。
现在尝试在SVM上执行相同操作:
$ ls Main.java $ javac -cp . Main.java $ ls Main.class Main.java $ native-image Main Build on Server(pid: 18438, port: 35415) classlist: 151.77 ms (cap): 1,662.32 ms setup: 1,880.78 ms error: Basic header file missing (<zlib.h>). Make sure libc and zlib headers are available on your system. Error: Processing image build request failed
好吧,是的。 这是因为我专门为“干净”测试制作了一个全新的虚拟机。
$ sudo apt-get install zlib1g-dev libc6 libc6-dev $ native-image Main Build on Server(pid: 18438, port: 35415) classlist: 135.17 ms (cap): 877.34 ms setup: 1,253.49 ms (typeflow): 4,103.97 ms (objects): 1,441.97 ms (features): 41.74 ms analysis: 5,690.63 ms universe: 252.43 ms (parse): 1,024.49 ms (inline): 819.27 ms (compile): 4,243.15 ms compile: 6,356.02 ms image: 632.29 ms write: 236.99 ms [total]: 14,591.30 ms
绝对的运行时数据可能令人恐惧。 但是,首先,这就是它的原意:非常可怕的优化在这里应用。 其次,这是您想要的脆弱的虚拟机。
最后,关键时刻:
$ ./main null
看来我们的客人没有说谎,真的行不通。
第一种方法:从主机窃取属性
然后,我在全局
os.version
搜索
os.version
,发现所有这些属性都在
SystemPropertiesSupport
类中。
我不会写该文件的完整路径,因为为IntelliJ IDEA和Eclipse生成正确项目的功能直接内置于SVM中。 这非常酷,完全不让人想起大多数OpenJDK必须经历的折磨。 让IDE为我们打开类。 因此:
public abstract class SystemPropertiesSupport { private static final String[] HOSTED_PROPERTIES = { "java.version", ImageInfo.PROPERTY_IMAGE_KIND_KEY, "line.separator", "path.separator", "file.separator", "os.arch", "os.name", "file.encoding", "sun.jnu.encoding", };
然后,我完全不加思索,只是
向该集合
添加了另一个变量:
"os.arch", "os.name", "os.version"
我重建,运行并获得令人垂涎的行
4.15.0-32-generic
。 万岁!
但这是问题所在:现在,在运行此代码的
每台计算机上,它总是发出
4.15.0-32-generic
。 即使在
uname -a
也会在旧的Ubunt上返回存储桶的先前版本。
很明显,这些变量在编译时已写入源文件。
实际上,您需要仔细阅读以下注释:
private static final String[] HOSTED_PROPERTIES
需要使用其他方法。
结论
- 如果要使“ main Java”中的系统属性出现在SVM中,这非常简单。 我们将所需的属性写在正确的位置,仅此而已。
- 您可以在IDE中工作,该IDE同时支持Java和Python。 例如,在具有Python插件的IntelliJ IDEA Ultimate中,或在Eclipse中相同。
第二种方法
如果浏览SystemPropertiesSupport
SystemPropertiesSupport
,我们会发现更加合理的东西:
private final Map<String, Supplier<String>> lazyRuntimeValues;
除其他外,使用这些属性仍然不会阻止构建可执行文件的过程。 显然,如果我们对
HOSTED_PROPERTIES
进行大量
HOSTED_PROPERTIES
,那么一切都会变慢。
通过引用返回以下内容的方法,惰性属性的注册以明显的方式发生:
lazyRuntimeValues.put("user.name", this::userNameValue); lazyRuntimeValues.put("user.home", this::userHomeValue); lazyRuntimeValues.put("user.dir", this::userDirValue);
而且,所有这些方法引用都是接口,并且对每个受支持的平台都实现了
this::userDirValue
。 在这种情况下,这些是
PosixSystemPropertiesSupport
和
WindowsSystemPropertiesSupport
。
如果我们出于好奇而无法使用Windows的实现,我们将看到可悲的事情:
@Override protected String userDirValue() { return "C:\\Users\\somebody"; }
如您所见,尚不支持Windows :-)但是,真正的问题是Windows可执行文件的生成尚未完成,因此支持这些方法实际上是完全不必要的工作。
也就是说,您需要实现以下方法:
lazyRuntimeValues.put("os.version", this::osVersionValue);
然后在两个或三个可用接口中支持它。
但是在那里写什么呢?
结论
- 如果要添加在运行时计算的新属性,则只需编写一个方法即可。 结果可能取决于当前的操作系统,切换机制已经在起作用并且没有询问。
一点考古
首先想到的是偷看OpenJDK中的实现并大胆地复制粘贴。 一点考古学和抢劫将永远不会阻止一个勇敢的探险家!
随意在Idea中打开任何Java项目,在其中编写
System.getProperty("os.version")
,然后按ctrl +单击转到
getProperty()
方法的实现。 事实证明,所有这一切都愚蠢地存在于
Properties
。
看起来,只需复制粘贴这些
Properties
被填充的地方,然后大笑起来,就会消失在空白中。 不幸的是,我们遇到一个问题:
private static native Properties initProperties(Properties props);
不,不,不。

但这一切开始得如此顺利。
有男孩吗?
众所周知,使用C ++是不好的。 SVM中使用C ++吗?
就这样! 甚至还有一个特殊的软件包:
src/com.oracle.svm.native
。
在此包中,horror-horror位于
getEnviron.c
文件中,如下所示:
extern char **environ; char **getEnviron() { return environ; }
是时候搞砸C ++了
现在深入研究并打开完整的OpenJDK源代码。
如果某人还没有,则可以
在网上查找或下载。 我警告您,他们仍然在Mercurial的帮助下
从这里摇摆,仍然需要大约半小时。
我们需要的文件位于
src/java.base/share/native/libjava/System.c
。
是否注意到这是文件的路径,而不仅仅是名称? 没错,您可以推销您新的闪亮时尚主意,该主意是每年200美元购买的。 您可以
尝试CLion ,但是为了避免不可逆转的精神损害,最好只使用
Visual Studio Code 。 他已经突出显示了某些内容,但仍然不明白自己所看到的内容(他没有将所有内容划掉红色)。
重播
System.c
:
java_props_t *sprops = GetJavaProperties(env)
依次将它们放在
src/java.base/unix/native/libjava/java_props_md.c
。
每个平台都有其自己的此类文件,它们通过
#define
切换。
从这里开始。 有很多平台。 由于GraalVM并未正式支持AIX,因此可以评分任何像AIX的死灵患者(据我所知,GNU-Linux,macOS和Windows最初计划在其中)。 GNU / Linux和Windows支持使用
<sys/utsname.h>
,它具有用于获取操作系统名称和版本的预定义方法。
但是macOS
的govnokod非常
糟糕 。
- 名称“ Mac OS X”用硬涂层表示(尽管它早已是macOS)。
- 这取决于makoshi的版本。 在10.9之前的版本中,SDK不具有
operatingSystemVersion
函数,您必须手动阅读SystemVersion.plist
。 - 对于此减法,它使用ObjC扩展名,如下所示:
如果最初有一个想法以良好的样式手动重写它,那么它很快就会变成现实。 如果我从ifs弄乱了面条的丛林中某个地方,有人会打碎它,然后将我吊死在中央广场怎么办? 好吧,nafig。
有必要复制粘贴。结论
- 不需要IDE;
- 与C ++的任何通信都是痛苦的,令人不愉快的,一见钟情。
复制粘贴是常态吗?
这是进一步折磨所依赖的重要问题。 我确实不想手动重写,但因违反许可而上法庭更为糟糕。 所以我去了github并直接问Codrut Stancu。 这是他的
回答 :
»重复使用OpenJDK代码,例如,从许可的角度来看,复制粘贴是正常的事情。 但是,这有一个很好的理由。 如果可以通过重复使用JDK代码而不复制来实现某个功能,例如用替换对其打补丁,那就更好了。”这听起来像是官方的复制粘贴许可!
我们正常交谈...
我开始移植这段代码,但遇到了懒惰。 要测试不同版本的macOS,您需要至少找到一个具有死灵性10.8 Mountain Lion的软件。 我有两个可用的苹果设备,一个是从朋友那里获得的,我还可以将其部署到一些试用版的VMWare中。
但是懒惰。 这种懒惰救了我。
我去了
聊天室 ,问克里斯·西顿,哪个工具链最适合组装。 支持哪个版本的OS,C ++编译器等等。
作为回应,他从聊天中获得了惊讶的沉默,克里斯回答说他不理解问题的实质。
克里斯花了很长时间才弄清楚我想做什么,并要求他
不再做 。
确实缺少SVM的想法。 SVM是纯Java,不应从OpenJDK插入代码。 您可以阅读它,并将其转换为Java,但是没有人想要OpenJDK的C ++代码。 那是我们想要的最后一件事。
数学库的示例无法说服他。 至少它们是用C编写的,而C ++的包含意味着将全新的语言连接到代码库。 还有就是fufufu。
你需要做什么? 用
System Java编写。
并且如果无法避免对C / C ++平台SDK的调用,那么这应该是包装在C API中的某些单个系统调用。 即使Platform SDK具有方便的现成方法来在C ++方面以不同的方式处理数据,都用Java提取数据,然后严格用Java编写业务逻辑。
我叹了口气,开始研究源代码,以了解如何以不同的方式完成此工作。
结论
- 与聊天中的人交谈,以了解任何晦涩的细节。 如果问题不是完全白痴,他们就会回答。 尽管此示例表明Chris准备好讨论白痴问题,即使这并不能节省他个人的时间;
- 项目中根本没有C ++。 没有理由相信有人会让他把他拖到地板下面;
- 相反,您需要使用C作为最后的手段在System Java中进行编写(例如,在调用平台SDK时)。
不需要小提琴手
亲爱的,不需要小提琴手。 他只吃多余的燃料。
然后我不知所措,因为看看这里。 如果在Windows上有
<sys/utsname.h>
,我们非常希望得到他的回答-这很简单。
但是,如果他不在那,那该怎么办?
- 调用cmd内置命令还是Windows实用程序? 用俄语发布需要解析的文本。 这是最底层的,可能与真正的OpenJDK在这里所要回答的不一致。
- 从注册表中拿走吗? 例如,即使在这里也有细微差别,例如,当从Windows 7切换到10时,在注册表中存储数字数字的方法也发生了变化,在Windows 10中,您需要粘住主要和次要组件的手,或者简单地回答它是一位数字的Windows 10。 尚不清楚哪种方法更正确(不会使用户的资产悔改)。
幸运的是,保罗·威格勒
( Paul Woegerer)提出的要求将我的痛苦
打断了 ,后者解决了所有问题。
有趣的是,起初,所有内容都在向导中修复(
os.version
停止在测试中提供
null
),然后才注意到pullrequest。 问题在于,该提交未在github上标记为pullrequest-这是一个简单的提交,注释中带有铭文
PullRequest: graal/1885
。 事实是,Oracle实验室的帅哥不使用Github,他们只需要它与外部提交者进行交互即可。 我们所有人都不够幸运,无法在Oracle Labs工作,我们需要订阅有关对存储库的新提交的通知,并全部阅读它们。
但是现在您可以放松下来,看看如何
正确实现此功能。
让我们看看System Java是什么样的野兽。
正如我之前说的,当他们试图敲掉牙齿时,一切都很简单,就像铲子的背面一样。 同样痛苦。 看一下池中的报价:
@Override protected String osVersionValue() { if (osVersionValue != null) { return osVersionValue; } CoreFoundation.CFDictionaryRef dict = CoreFoundation._CFCopyServerVersionDictionary(); if (dict.isNull()) { dict = CoreFoundation._CFCopySystemVersionDictionary(); } if (dict.isNull()) { return osVersionValue = "Unknown"; } CoreFoundation.CFStringRef dictKeyRef = DarwinCoreFoundationUtils.toCFStringRef("MacOSXProductVersion"); CoreFoundation.CFStringRef dictValue = CoreFoundation.CFDictionaryGetValue(dict, dictKeyRef); CoreFoundation.CFRelease(dictKeyRef); if (dictValue.isNull()) { dictKeyRef = DarwinCoreFoundationUtils.toCFStringRef("ProductVersion"); dictValue = CoreFoundation.CFDictionaryGetValue(dict, dictKeyRef); CoreFoundation.CFRelease(dictKeyRef); } if (dictValue.isNull()) { return osVersionValue = "Unknown"; } osVersionValue = DarwinCoreFoundationUtils.fromCFStringRef(dictValue); CoreFoundation.CFRelease(dictValue); return osVersionValue; }
换句话说,我们用Java逐字写了用C语言写的东西。
DarwinExecutableName
一下
DarwinExecutableName
写法:
@Override public Object apply(Object[] args) { final CIntPointer sizePointer = StackValue.get(CIntPointer.class); sizePointer.write(0); if (DarwinDyld._NSGetExecutablePath(WordFactory.nullPointer(), sizePointer) != -1) { VMError.shouldNotReachHere("DarwinExecutableName.getExecutableName: Executable path length is 0?"); } final byte[] byteBuffer = new byte[sizePointer.read()]; try (PinnedObject pinnedBuffer = PinnedObject.create(byteBuffer)) { final CCharPointer bufferPointer = pinnedBuffer.addressOfArrayElement(0); if (DarwinDyld._NSGetExecutablePath(bufferPointer, sizePointer) == -1) { return null; } final String executableString = CTypeConversion.toJavaString(bufferPointer); final String result = realpath(executableString); return result; } }
所有这些
CIntPointer
,
CCharPointer
,
PinnedObject
。
就我的口味而言,这不舒服且丑陋。 您需要手动使用类似于Java类的指针。 您需要及时调用适当的
release
,以免内存泄漏。
但是,如果您认为这些是
不合理的措施,那么您可以再次查看
.NET中GC的
实现,并且如果您不及时停止,就会对C ++导致的结果感到震惊。 我提醒您,这是一个很大的CPP文件,大于一个兆字节。 关于他的工作有
一些描述 ,但是显然不足以使外部贡献者理解。 上面的代码虽然看起来很讨厌,但可以通过Java的静态分析来很好地理解和分析。
至于承诺的实质,我对他有疑问。 至少那里没有实现Windows支持。 当出现Windows的代码源时,我将尝试执行此任务。
结论
- 需要用System Java编写。 赞美,叫甜面包。 仍然没有选择。
- 从GitHub上的存储库订阅通知并阅读提交,否则重要的PR将会飞逝;
- 如果可能,请询问负责此领域的人员的主要功能。 有很多实现的方法,但是它们尚未为大众所知。 有机会发明一辆自行车,这比甲骨文实验室的家伙制造的自行车差得多。
- 处理功能时,请务必在github上通知负责人。 如果他不回答-写封信,所有团队成员的地址都可以轻松地用Google搜索。
结语
这场战斗结束了,但战争根本没有结束。
战斗机,敏感地等待有关哈布雷的新文章并
进入我们的行列 !
我想提醒您,Oracle唯一的GraalVM
官方推广者Oleg Shelaev将参加
下一次Joker会议 。 不仅是“唯一的俄语使用者”,而且是“总体上唯一的俄语使用者”。 该报告的标题(
“使用GraalVM提前编译Java” )提示SubstrateVM不能没有。
顺便说一句,奥列格(Oleg)最近获得了一种服务武器-在
谢拉耶夫·奥勒格(Haslaé)的帐户上。 尚无帖子,但您可以使用此用户名进行投射。
您可以在Telegram的聊天室聊天中与Oleg和Oleg交谈:
@graalvm_ru 。 与Github上的ishshuyev不同,您可以在其中进行任何形式的交流,并且不会有人被禁止(
但这并不准确 )。
我还提醒您,每周我们都会与Debriefing播客一起发布Java Digest。 例如,这是
最后的摘要 。 有时,有关GraalVM的新闻会不时出现(事实上,我不会因为尊重观众而将整个问题转为GraalVM新闻发布:-)
感谢您阅读此书-很快再见!