在2024年Clojure状态调查[https://www.surveymonkey.com/r/clojure2024]中分享您的想法!

欢迎!请查看关于页面,了解更多有关本网站如何运作的信息。

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 发表

我能够在这些Java 6 JVMs上重现您看到的行为,这些都在Ubuntu 12.04.2上

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™ SE运行时环境(构建1.6.0_45-b06)
Java HotSpot™ 64位服务器VM(构建20.45-b01,混合模式)

然而,我尝试了两种Java 7 JVM,并表现出以下行为,看起来更接近您的期望。我不知道Java 6和Java 7之间的精确差异导致了这种行为差异,但这确实是有关于Java 6与Java 7之间某些关联的证据。

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

用户=> (定义函数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 TM SE运行环境(构建1.7.0_21-b11)
Java HotSpot TM 64位服务器JVM(构建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位服务器JVM(构建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; //class 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 方法中,调用getMethods()后随后调用invokeMatchingMethod()。getMethods()在Java 6和Java 7上以不同的顺序返回4个不同版本的java.lang.Math/abs方法,导致在两个JVM上选择不同的一种。

java版本 "1.6.0_39"
Java TM SE运行环境(构建1.6.0_39-b04)
Java HotSpot TM 64位服务器JVM(构建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位服务器JVM(构建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 Runtime Environment (build 1.8.0_45-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode) [~]> 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报告)
...