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

我能在这些 Java 6 JVM(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的区别有关。

user=> (set! warn-on-reflection true)
true
user=> (defn f (link: a b) (Math/abs (- a b)))
反射警告,NO_SOURCE_PATH:1:15 - abs 方法的调用无法解析。

'user/f

user=> (f 1000000000000 2000000000000)
1000000000000
user=> (class (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的工作方式与Java 6有所不同?

以下是生成的字节码(供大家参考)

public java.lang.Object invoke(java.lang.Object);
  代码
   0:     ldc     #14; // 字符串 java.lang.Math
   2:     invokestatic     #20; // 方法 java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
   5:     ldc     #22; // 字符串 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下真的就是这样?)但鉴于这是一个静态方法,所有可用的重载变体在编译时可能都已为人所知,也许它可以生成如下代码

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

0
五月29, 2013

评论由:jafingerhut

在Reflector.java的invokeStaticMethod(Class c, String methodName, Object)方法中,存在对getMethods() 的调用,紧随其后是对invokeMatchingMethod() 的调用。getMethods() 在Java 6和7中返回不同的java.lang.Math/abs方法的顺序,导致在两个JVM上invokeMatchingMethod()选择的方法不同。

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

user=> (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,混合模式)

user=> (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版本 "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 - 调用静态方法java.lang.Math.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报告)
...