Java Double.valueOf
Posted
技术标签:
【中文标题】Java Double.valueOf【英文标题】: 【发布时间】:2020-07-14 17:10:12 【问题描述】:在我的工作中,所有开发人员都使用Double.valueOf
而不是new Double
构造函数。在任何情况下。对于 Integer 或 Short 我可以理解它的缓存值,但不适用于 double
和 float
。
我看 Double.valueOf in OpenJDK sources :
/**
* Returns a @code Double instance representing the specified
* @code double value.
* If a new @code Double instance is not required, this method
* should generally be used in preference to the constructor
* @link #Double(double), as this method is likely to yield
* significantly better space and time performance by caching
* frequently requested values.
*
* @param d a double value.
* @return a @code Double instance representing @code d.
* @since 1.5
*/
public static Double valueOf(double d)
return new Double(d);
所以它只是调用Double
s 构造函数。
我可以理解为什么存在该方法以与 Integer/Short 保持一致...
但是方法上的注释:
此方法一般应优先于构造函数使用
在我看到它对每个 valueOf()
方法的相同评论之后。 (整数,短...)
评论是假的?还是其他进程干预?
我听说过 Java 中的内部方法,下一步是在热点代码和 vmSymbol.hpp 中搜索
/* boxing methods: */ \
do_name( valueOf_name, "valueOf") \
do_intrinsic(_Boolean_valueOf, java_lang_Boolean, valueOf_name, Boolean_valueOf_signature, F_S) \
do_name( Boolean_valueOf_signature, "(Z)Ljava/lang/Boolean;") \
do_intrinsic(_Byte_valueOf, java_lang_Byte, valueOf_name, Byte_valueOf_signature, F_S) \
do_name( Byte_valueOf_signature, "(B)Ljava/lang/Byte;") \
do_intrinsic(_Character_valueOf, java_lang_Character, valueOf_name, Character_valueOf_signature, F_S) \
do_name( Character_valueOf_signature, "(C)Ljava/lang/Character;") \
do_intrinsic(_Short_valueOf, java_lang_Short, valueOf_name, Short_valueOf_signature, F_S) \
do_name( Short_valueOf_signature, "(S)Ljava/lang/Short;") \
do_intrinsic(_Integer_valueOf, java_lang_Integer, valueOf_name, Integer_valueOf_signature, F_S) \
do_name( Integer_valueOf_signature, "(I)Ljava/lang/Integer;") \
do_intrinsic(_Long_valueOf, java_lang_Long, valueOf_name, Long_valueOf_signature, F_S) \
do_name( Long_valueOf_signature, "(J)Ljava/lang/Long;") \
do_intrinsic(_Float_valueOf, java_lang_Float, valueOf_name, Float_valueOf_signature, F_S) \
do_name( Float_valueOf_signature, "(F)Ljava/lang/Float;") \
do_intrinsic(_Double_valueOf, java_lang_Double, valueOf_name, Double_valueOf_signature, F_S) \
do_name( Double_valueOf_signature, "(D)Ljava/lang/Double;")
所以在我在源代码中 grep Double.valueOf
后,我找到了 withebox.cpp
static jobject doubleBox(JavaThread* thread, JNIEnv* env, jdouble value)
return box(thread, env, vmSymbols::java_lang_Double(), vmSymbols::Double_valueOf_signature(), value);
和盒子方法代码:
template <typename T>
static jobject box(JavaThread* thread, JNIEnv* env, Symbol* name, Symbol* sig, T value)
ResourceMark rm(thread);
jclass clazz = env->FindClass(name->as_C_string());
CHECK_JNI_EXCEPTION_(env, NULL);
jmethodID methodID = env->GetStaticMethodID(clazz,
vmSymbols::valueOf_name()->as_C_string(),
sig->as_C_string());
CHECK_JNI_EXCEPTION_(env, NULL);
jobject result = env->CallStaticObjectMethod(clazz, methodID, value);
CHECK_JNI_EXCEPTION_(env, NULL);
return result;
还有这段代码……
好的,我还没有进步。这个方法box()真的是在服务器模式下调用的吗?
简历的3个问题:D
JVM拦截Double.valueOf()
调用这个盒子方法?
Double.valueOf
不使用 JDK 源并且不使用 new Double()
?
Double.valueOf()
上的评论只是转储复制/粘贴,Double.valueOf
和new Double
效果一样吗?
【问题讨论】:
来自文档:“如果不需要新的 Double 实例,则通常应优先使用此方法而不是构造函数 Double(double),因为此方法可能会产生更好的效果通过缓存频繁请求的值来提高空间和时间性能" -valueOf
允许重用值。是的,还有另一个“干预”过程,即享元系统,其中相同值的对象被重用(不是所有值;通常在字节范围内),而不是构造新对象。
如果你好奇为什么valueOf
只是new Double
,那是因为Double#valueOf
有一个intrinsic method,它通常在运行时使用。但是,如果禁用此类优化,则必须有默认实现。返回 new Double
是默认实现。
这能回答你的问题吗? Why do java intrinsic functions still have code?
【参考方案1】:
Integer(int)
、Double(double)
和类似的构造函数已被弃用(是的,它们实际上被标记为 @Deprecated
),因为 Java 9 作为 JEP 277 的一部分。
deprecation comment 告诉你
静态工厂
valueOf(double)
通常是更好的选择,因为它可能会产生明显更好的空间和时间性能。
虽然 OpenJDK 目前不缓存盒装双精度,但静态工厂方法为未来的优化开辟了道路。如果决定在 JVM 或类库中改进双精度的自动装箱,应用程序将自动从优化中受益,而无需从用户端进行任何更改。
另一个原因是Double.valueOf
确实是HotSpot中的一个内在方法。 JVM knows 关于自动装箱方法,并在EliminateAutoBox 优化中使用了这些知识。
有两个相关的优化:EliminateAllocations 和 EliminateAutoBox。尽管它们都由 Escape Analysis 提供并且看起来相似,但它们应用在稍微不同的上下文中,因此可能会发生一种优化有效而另一种无效的情况,反之亦然。
当自动装箱消除成功时,例如当 JIT 编译器发现匹配的 valueOf
和 doubleValue
调用,并且对象没有转义时,优化器会完全摆脱这两个调用。
顺便说一句,提到的Whitebox 代码无关紧要。它仅用于内部 HotSpot 测试目的。
Valhalla 项目目前正在积极开发中,它引发了对原始包装器的全面重新思考。
current idea 用于制作int
、double
等inline types,以及Integer
和Double
它们的参考投影。作为迁移步骤,Integer
和 Double
将成为密封抽象类,因此无法通过构造函数实例化它们。
因此,将弃用构造函数视为 Valhalla 项目的中间步骤。通过鼓励使用工厂方法,JDK 开发人员为他们可以在后台进行的许多优化扫清了道路,包括尽可能内联 Double
实例。
【讨论】:
【参考方案2】:简短的回答:一致性。
这是一个你在编码时应该努力的概念,因为它减少了出错的机会,并使代码更容易阅读。
此外,您永远不知道可能随时添加哪些优化,因此请使用valueOf()
方法,除非您需要一个新的Double
值(即极为罕见)。仅仅因为它现在不缓存公共值,并不意味着它不会在未来或在不同的实现中。
这是一个叫做future-proofing的概念,也是你编码时应该努力的东西。
【讨论】:
以上是关于Java Double.valueOf的主要内容,如果未能解决你的问题,请参考以下文章