繁体   English   中英

JVM上的编译时引用参数

[英]Compile-Time By-Reference Parameters on the JVM

目前,我正在基于JVM上的自定义编程语言进行开发,我希望该语言支持方法中的按引用参数。 我将如何去做? 到目前为止,我能够提出三种不同的方法来完成此任务。

  1. 包装对象

这背后的想法是创建一个包装对象,该对象创建时包含字段的当前值,并传递给by-ref方法调用,然后在调用后取消装箱。 这是一种相当简单的方法,但是需要创建并立即丢弃很多“垃圾”对象。

  1. 数组

只需创建一个带有1个元素的类型的数组,将字段值放入数组中,调用传递数组的方法,最后从数组中分配字段。 这样做的好处是,它确保了运行时类型安全,而不是泛型包装器类,因为它需要额外的强制转换。

  1. 不安全

这一点稍微先进一点:使用sun.misc.Unsafe分配一些本机内存空间,将字段值存储在该内存中,调用该方法并传递地址,从本机内存地址重新分配该字段,然后释放它再次上升。

优点 :是否可以使用Unsafe类实现指针和指针算术?

包装器对象,但是需要创建并立即丢弃很多“垃圾”对象。

如果此类包装器的生命周期仅限于一个调用站点(+内联被调用方),则编译器可以通过转义分析来证明这一点,并通过将包装器对象分解为其原始成员并在生成的代码中直接使用它们来避免分配。

这本质上要求那些引用包装器永远不要存储到字段中,而只能作为方法参数传递

不安全使用sun.misc.Unsafe分配一些本机内存空间,将字段值存储在该内存中

您不能在本机内存中存储对象引用。 垃圾收集器对此一无所知,因此可以更改您脚下的内存地址,也可以更改GC对象(如果这是唯一的参考)。

但是,由于您要创建自己的语言,因此可以简单地将字段引用糖化为对象引用+偏移量。 即,传递两个参数(对象ref +长偏移量)而不是一个。 如果您知道偏移量,则可以使用“不安全”来操纵该字段。

显然,这仅适用于对象字段。 本地引用不能以这种方式更改。

奖励:是否可以使用Unsafe类实现指针和指针算术?

是,用于非托管内存。

对于托管堆中的内存,您只能指向对象本身,并相对于对象标头执行指针算术运算。
而且,您始终必须将对象引用存储在Object类型的字段中。 long存储它们会导致GC实现(至少是精确实现)缺少参考。


编辑:您可能还对JDK中有关VarHandles的正在进行的工作感兴趣 在开发语言时,可能要牢记这一点。

似乎您错过了有关按引用传递概念的重要要点: 每当对引用进行写操作时,所引用的变量都会被更新。 这与像您这样的概念不同,它们实际上会在持有人中传递副本,并在方法返回时更新原始变量。

您甚至可以在单线程用例中注意到差异:

foo(myField, ()-> {
    // if myField is pass-by-reference, whenever foo() modifies
    // it and calls this Runnable, it should see the new value:
    System.out.println(myField);
});

当然,您可以使两个引用都访问同一包装器,但是对于允许(几乎)任意代码的环境,这意味着您必须替换对字段的所有引用(最后,更改字段的内容)到包装纸。


因此,如果您想在JVM中实现一种干净,真实的按值传递机制,它必须能够修改引用的工件,即字段或数组插槽。 对于局部变量,无法执行此操作,因此一旦创建了对它的引用,就无法用holder对象替换局部变量。

因此,这种类型的选项是已知的,您可以传递java.lang.reflect.Field (不适用于数组插槽),一对java.lang.invoke.MethodHandle或任意类型的对象(生成的类型)提供读写访问权限。

在实现此引用访问器类型时,可以像Java的lambda表达式工具一样,使用Unsafe创建一个匿名类。 如果可以的话 通过lambda表达机制为自己带来了很多启发:

  • 在必须创建引用的地方放置一个invokedynamic指令,指向您的工厂方法并提供对字段或数组插槽的句柄
  • 让工厂分析句柄并动态创建访问器实现,主要区别在于您的类型将具有两个操作,即读取和写入
  • 使用Unsafe创建该类(即使它是private ,也可以访问该字段)
  • 如果该字段是静态的,则创建一个实例并返回带有返回该实例的句柄的CallSite
  • 否则,返回一个CallSite ,其句柄指向接受对象实例或数组的访问器类的构造函数

这样,您仅在第一次使用时会有开销,而后续使用将在static字段的情况下使用单例,或者为实例字段和数组插槽static构建访问器。 如果像普通对象一样频繁使用,则可以通过HotSpots转义分析来消除这些访问器实例的创建。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM