2024 Clojure 状态调查! 中分享您的想法。

欢迎!请查看 关于 页面以获取更多有关如何使用本站的信息。

0
编译器

我意识到在调用这些方法时依赖于反射从性能上讲不是最佳选择,但这不应该导致不正确或危险的行为。

在这里,它似乎触发了对输入 long 的静默向下转型,从而产生截断的整数输出

user> (defn f (link: a b) (Math/abs (- a b)))
反射警告,NO_SOURCE_PATH:1:15 - 无法解析对 abs 的调用。

'user/f

user> (f 1000000000000 2000000000000)
727379968
user> (class (f 1000000000000 2000000000000))
java.lang.Integer
user> (defn f (link: ^long a ^long b) (Math/abs (- a b)))

'user/f

user> (f 1000000000000 2000000000000)
1000000000000
user> (class (f 1000000000000 2000000000000))
java.lang.Long

9 答案

0

评论由:matthjw

以下是一种更简单的复现问题的方法

user> (#(Math/abs %) 1000000000000)
反射警告,NO_SOURCE_PATH:1:3 - 无法解析对 abs 的调用。
727379968

0

评论由:jafingerhut

我在 Ubuntu 12.04.2 上的这些 Java 6 JVM 复现了您看到的行为

java version "1.6.0_27"
OpenJDK 运行环境(IcedTea6 1.12.5)(6b27-1.12.5-0ubuntu0.12.04.1)
OpenJDK 64位服务器 VM(构建 20.0-b12,混合模式)

java version "1.6.0_45"
Java(TM) SE 运行环境(构建 1.6.0_45-b06)
Java HotSpot(TM) 64位服务器 VM(构建 20.45-b01,混合模式)

然而,我试了两个Java 7 JVMs,它给出了以下行为,看起来更接近你所期望的。我不知道Java 6和Java 7之间导致这种行为差异的确切区别是什么,但这是一些证据表明这可能与Java 6与Java 7有关。

用户=> (set! warn-on-reflection true)

用户=> (defn f (链接: a b) (Math/abs (- a b)))
反射警告,NO_SOURCE_PATH:1:15 - 无法解析对 abs 的调用。

'user/f

用户=> (f 1000000000000 2000000000000)
1000000000000
用户=> (class (f 1000000000000 2000000000000))
java.lang.Long

以上行为在Clojure 1.5.1和这些JVMs上观察到

Ubuntu 12.04.2加上这个JVM
java版本 "1.7.0_21"
Java(TM) SE运行时环境(构建1.7.0_21-b11)
Java HotSpot(TM) 64位服务器VM(构建23.21-b01,混合模式)

Mac OS X 10.8.3加上这个JVM
java版本 "1.7.0_15"
Java(TM) SE运行时环境(构建1.7.0_15-b03)
Java HotSpot(TM) 64位服务器VM(构建23.7-b01,混合模式)

0
评论者:matthjw_

啊,有趣。
也许这是Java 7中反射API工作的方式差异?

以下是生成的字节码,供好奇的人参考

public java.lang.Object invoke(java.lang.Object);
  代码
   0: ldc     #14; //String java.lang.Math
   2: invokestatic     #20; //Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
   5: ldc     #22; //String abs
   7: iconst_1
   8: anewarray     #24; //class java/lang/Object
   11: dup
   12: iconst_0
   13: aload_1
   14: aconst_null
   15: astore_1
   16: aastore
   17: invokestatic     #30; //Method clojure/lang/Reflector.invokeStaticMethod:(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
   20: areturn
0

评论由:matthjw

只是一个建议(也许这就是Java 7下正在发生的事情?))给定它是静态方法,所有可用的重载变体可能在编译时已知,也许它可以生成类似以下代码

(cond
(instance? Long x) (Math/abs (long x)))
(instance? Integer x) (Math/abs (int x)))
;; ...
)

0

评论由:jafingerhut

在 `Reflector.java` 类的 `invokeStaticMethod` 方法(参数为 `Class c`, `String methodName`, `Object(link: ) args`)中,存在对 `getMethods()` 的调用,之后是对 `invokeMatchingMethod()` 的调用。`getMethods()` 在 Java 6 和 7 上返回不同顺序的 4 个 `java.lang.Math/abs` 方法,导致在两个 JVM 上 `invokeMatchingMethod()` 选择不同的方法。

java 版本 "1.6.0_39"
Java(SE) 运行环境(构建 1.6.0_39-b04)
Java HotSpot(SE) 64位服务器虚拟机(构建 20.14-b01,混合模式)

用户=> (pprint (seq (clojure.lang.Reflector/getMethods java.lang.Math 1 "abs" true)))
(#
#
#
#)
nil

java版本 "1.7.0_21"
Java(TM) SE运行时环境(构建1.7.0_21-b11)
Java HotSpot(TM) 64位服务器VM(构建23.21-b01,混合模式)

用户=> (pprint (seq (clojure.lang.Reflector/getMethods java.lang.Math 1 "abs" true)))
(#
#
#
#)
nil

这可能表明 `invokeMatchingMethod()` 中的不良行为,该行为过于依赖它的方法的顺序。

正如你提到的,类型提示有助于避免反射引起的重大性能损失。

0

评论由:alexmiller 提出

在 1.8.0-alpha3 上无法重现。

0

评论由:bronsa 提出

Alex,我使用 1.8.0-master-SNAPSHOT 和 jdk 1.8 可以重现。

[~]> java -version java version "1.8.0_45" Java(TM) SE 运行环境(构建 1.8.0_45-b14)Java HotSpot(TM) 64位服务器虚拟机(构建 25.45-b02,混合模式)[~]> clj Clojure 1.8.0-master-SNAPSHOT user=> (set! *warn-on-reflection* true) true user=> (#(Math/abs %) 1000000000000) 反射警告,NO_SOURCE_PATH:2:3 - 调用静态方法 abs on java.lang.Math 无法解析(参数 727379968 user=> (class *1) java.lang.Integer

Andy 最近的评论提到,clojure.lang.Reflector.invokeStaticMethod 依赖于传递给它的方法的顺序,并且这个顺序可能在不同的 JDK 版本之间发生变化,这可能是你无法重现它的原因。

0

评论由:jafingerhut

可能与 CLJ-1921 相同,或者至少有一些相似之处。

0
参考:https://clojure.atlassian.net/browse/CLJ-1212(由 alex+import 提出)
...