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

欢迎!请参阅关于页面了解有关此功能的一些更多信息。

0
编译器

我意识到在调用这些方法时依赖反射并不是性能上的好主意,但它不应该导致不正确或危险的行为。

在这里,它似乎触发了输入长数的静默向下转型,给出截断的整数输出

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 版本 "1.6.0_27"
OpenJDK 运行时环境(IcedTea6 1.12.5)(6b27-1.12.5-0ubuntu0.12.04.1)
OpenJDK 64 位服务器 VM(构建 20.0-b12,混合模式)

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

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

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

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

'user/f

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

在以下JVM上观察到上述行为:Clojure 1.5.1

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

Mac OS X 10.8.3加上此JVM
java版本"1.7.0_15"
Java™ SE运行时环境(构造1.7.0_15-b03)
Java HotSpot™ 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; //方法 java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
   5: ldc #22; //String abs
   7: iconst_1
   8: anewarray #24; //类 java/lang/Object
   11: dup
   12: iconst_0
   13: aload_1
   14: aconst_null
   15: astore_1
   16: aastore
   17: invokestatic #30; //方法 clojure/lang/Reflector.invokeStaticMethod:(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
   20: areturn
0

评论由:matthjw 提出

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

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

0

评论由:jafingerhut 提出

在 Reflector.java 中调用 getMethods() 方法后,再调用 invokeMatchingMethod() 方法。getMethods() 在Java 6和Java 7上以不同的顺序返回4个 java.lang.Math/abs 方法,导致 invokeMatchingMethod() 在两个JVM上选择不同的一个

java版本"1.6.0_39"
Java™ SE运行时环境(构造1.6.0_39-b04)
Java HotSpot 64-Bit Server VM (构建 20.14-b01,混合模式)

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

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

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

这可能是 invokeMatchingMethod() 方法中不理想行为的一个迹象,该方法过度依赖于传入的方法顺序。

正如您所说,类型提示有助于避免反射带来的重大性能损失。

0
by

由 alexmiller 发布评论

在 1.8.0-alpha3 中无法复现。

0
by

由 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 64-Bit Server VM (构建 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 - 调用 java.lang.Math 的 static 方法 abs 无法解析 (参数 727379968 user=> (class *1) java.lang.Integer

Andy 的最后一条评论提到 clojure.lang.Reflector.invokeStaticMethod 依赖于传入的方法顺序,并且这种顺序可以在 JDK 版本之间发生变化,这可能是您无法复现的原因。

0
by

评论由:jafingerhut 提出

可能与 CLJ-1921 相同,或至少有一些共同点。

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