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 位服务器虚拟机(构建 20.0-b12,混合模式)

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

但是,我尝试了两个Java 7 JVM,并得到了以下看似更符合你期望的行为。我不知道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
用户=> (类别 (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(TM) 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(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; //方法 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下发生的事情?),鉴于它是一个静态方法,并且所有可用的重载变体似乎在编译时都是已知的,也许它可以生成类似的代码

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

0票数

由:jafingerhut 添加评论

在 Reflector.java 的 invokeStaticMethod(Class c, String methodName, Object(link: ) args) 方法中,调用了 getMethods() 后紧接着调用了 invokeMatchingMethod()。getMethods() 在Java 6和Java 7中的不同顺序返回了 4 个 java.lang.Math/abs 方法,导致两个JVM在 invokeMatchingMethod() 上选择了不同的方法

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

user=> (打印 (序列 (clojure.lang.Reflector/getMethods java.lang.Math 1 "abs" true)))
(#
#
#
#)
空值

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

user=> (打印 (序列 (clojure.lang.Reflector/getMethods java.lang.Math 1 "abs" true)))
(#
#
#
#)
空值

这可能是invokeMatchingMethod()中不希望的行为的迹象,它过于依赖给予它的方法顺序。

正如你提到的,类型提示对于避免反射带来的显著性能下降是有好处的。

0票数

评论者:alexmiller

在1.8.0-alpha3版本中无法重现。

0票数

评论者:bronsa

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

[~]> java -version java版本"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在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 (报告人:alexmiller+import)
...