Java 转换会带来开销吗?为什么?

发布于 2024-08-19 22:21:30 字数 354 浏览 1 评论 0原文

当我们将一种类型的对象转换为另一种类型时,是否有任何开销?或者编译器只是解决所有问题并且在运行时没有任何成本?

这是普遍现象还是有不同情况?

例如,假设我们有一个 Object[] 数组,其中每个元素可能具有不同的类型。但我们总是确定,比如说,元素 0 是 Double,元素 1 是 String。 (我知道这是一个错误的设计,但我们假设我必须这样做。)

Java 的类型信息在运行时仍然保留吗?或者编译后一切都会被忘记,如果我们执行 (Double)elements[0],我们将只跟随指针并将这 8 个字节解释为双精度数,无论那是什么?

我非常不清楚 Java 中的类型是如何完成的。如果您对书籍或文章有任何推荐,也谢谢。

Is there any overhead when we cast objects of one type to another? Or the compiler just resolves everything and there is no cost at run time?

Is this a general things, or there are different cases?

For example, suppose we have an array of Object[], where each element might have a different type. But we always know for sure that, say, element 0 is a Double, element 1 is a String. (I know this is a wrong design, but let's just assume I had to do this.)

Is Java's type information still kept around at run time? Or everything is forgotten after compilation, and if we do (Double)elements[0], we'll just follow the pointer and interpret those 8 bytes as a double, whatever that is?

I'm very unclear about how types are done in Java. If you have any reccommendation on books or article then thanks, too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

行雁书 2024-08-26 22:21:30

有两种类型的转换:

隐式转换,当您从一种类型转换为更广泛的类型时,这是自动完成的,并且没有任何开销:

String s = "Cast";
Object o = s; // implicit casting

显式转换,当您从较宽的类型变为较窄的类型。对于这种情况,您必须像这样显式地使用强制转换:

Object o = someObject;
String s = (String) o; // explicit casting

在第二种情况下,运行时会产生开销,因为必须检查两种类型,并且如果强制转换不可行,JVM 必须抛出 ClassCastException。

摘自JavaWorld:转换成本< /a>

铸造用于在之间进行转换
types -- 之间的引用类型
特别是对于铸造类型
我们感兴趣的操作
这里。

Upcast 操作(也称为
扩大 Java 中的转换
语言规范)转换a
子类对祖先的引用
类参考。这次选角
操作通常是自动的,因为
它总是安全的并且可以
由编译器直接实现。

沮丧操作(也称为
缩小 Java 中的转换范围
语言规范)转换
祖先类对子类的引用
参考。此次铸造作业
产生执行开销,因为 Java
要求在以下位置检查演员阵容
运行时以确保其有效。
如果引用的对象不是
目标类型的实例
该类型的强制转换或子类,
不允许尝试强制转换
并且必须抛出一个
java.lang.ClassCastException。

There are 2 types of casting:

Implicit casting, when you cast from a type to a wider type, which is done automatically and there is no overhead:

String s = "Cast";
Object o = s; // implicit casting

Explicit casting, when you go from a wider type to a more narrow one. For this case, you must explicitly use casting like that:

Object o = someObject;
String s = (String) o; // explicit casting

In this second case, there is overhead in runtime, because the two types must be checked and in case that casting is not feasible, JVM must throw a ClassCastException.

Taken from JavaWorld: The cost of casting

Casting is used to convert between
types -- between reference types in
particular, for the type of casting
operation in which we're interested
here.

Upcast operations (also called
widening conversions in the Java
Language Specification) convert a
subclass reference to an ancestor
class reference. This casting
operation is normally automatic, since
it's always safe and can be
implemented directly by the compiler.

Downcast operations (also called
narrowing conversions in the Java
Language Specification) convert an
ancestor class reference to a subclass
reference. This casting operation
creates execution overhead, since Java
requires that the cast be checked at
runtime to make sure that it's valid.
If the referenced object is not an
instance of either the target type for
the cast or a subclass of that type,
the attempted cast is not permitted
and must throw a
java.lang.ClassCastException.

风吹过旳痕迹 2024-08-26 22:21:30

对于 Java 的合理实现:

每个对象都有一个标头,其中包含指向运行时类型的指针(例如 DoubleString,但它永远不可能是CharSequenceAbstractList)。假设运行时编译器(在 Sun 的情况下通常为 HotSpot)无法静态确定类型,则需要通过生成的机器代码执行一些检查。

首先需要读取指向运行时类型的指针。无论如何,这对于在类似情况下调用虚拟方法是必要的。

对于转换为类类型,在您点击 java.lang.Object 之前,确切知道有多少个超类,因此可以在距类型指针(实际上是第一个)的常量偏移处读取该类型。 HotSpot 中为 8 个)。这又类似于读取虚拟方法的方法指针。

然后读取的值只需要与预期的静态类型进行比较。根据指令集架构,另一条指令将需要在不正确的分支上进行分支(或故障)。诸如 32 位 ARM 之类的 ISA 具有条件指令,也许能够让悲伤的路径穿过快乐的路径。

由于接口的多重继承,接口变得更加困难。通常,对接口的最后两次强制转换会缓存在运行时类型中。在早期(十多年前),界面有点慢,但这已不再重要。

希望您能看到这类事情很大程度上与性能无关。你的源代码更重要。就性能而言,您的场景中最大的打击可能是由于到处追逐对象指针而导致缓存未命中(类型信息当然是常见的)。

For a reasonable implementation of Java:

Each object has a header containing, amongst other things, a pointer to the runtime type (for instance Double or String, but it could never be CharSequence or AbstractList). Assuming the runtime compiler (generally HotSpot in Sun's case) cannot determine the type statically a some checking needs to be performed by the generated machine code.

First that pointer to the runtime type needs to be read. This is necessary for calling a virtual method in a similar situation anyway.

For casting to a class type, it is known exactly how many superclasses there are until you hit java.lang.Object, so the type can be read at a constant offset from the type pointer (actually the first eight in HotSpot). Again this is analogous to reading a method pointer for a virtual method.

Then the read value just needs a comparison to the expected static type of the cast. Depending upon instruction set architecture, another instruction will need to branch (or fault) on an incorrect branch. ISAs such as 32-bit ARM have conditional instruction and may be able to have the sad path pass through the happy path.

Interfaces are more difficult due to multiple inheritance of interface. Generally the last two casts to interfaces are cached in the runtime type. IN the very early days (over a decade ago), interfaces were a bit slow, but that is no longer relevant.

Hopefully you can see that this sort of thing is largely irrelevant to performance. Your source code is more important. In terms of performance, the biggest hit in your scenario is liable to be cache misses from chasing object pointers all over the place (the type information will of course be common).

裸钻 2024-08-26 22:21:30

例如,假设我们有一个 Object[] 数组,其中每个元素可能具有不同的类型。但我们总是确定,比如说,元素 0 是 Double,元素 1 是 String。 (我知道这是一个错误的设计,但我们假设我必须这样做。)

编译器不会注意到数组中各个元素的类型。它只是检查每个元素表达式的类型是否可分配给数组元素类型。

Java 的类型信息在运行时仍然保留吗?或者编译后一切都会被忘记,如果我们执行 (Double)elements[0],我们将只跟随指针并将这 8 个字节解释为双精度数,无论是什么?

一些信息在运行时保留,但不保留各个元素的静态类型。您可以通过查看类文件格式来判断这一点。

理论上,JIT 编译器可以使用“转义分析”来消除某些赋值中不必要的类型检查。但是,按照您建议的程度执行此操作将超出实际优化的范围。分析单个元素类型的回报太小了。

此外,人们无论如何都不应该编写这样的应用程序代码。

For example, suppose we have an array of Object[], where each element might have a different type. But we always know for sure that, say, element 0 is a Double, element 1 is a String. (I know this is a wrong design, but let's just assume I had to do this.)

The compiler does not note the types of the individual elements of an array. It simply checks that the type of each element expression is assignable to the array element type.

Is Java's type information still kept around at run time? Or everything is forgotten after compilation, and if we do (Double)elements[0], we'll just follow the pointer and interpret those 8 bytes as a double, whatever that is?

Some information is kept around at run time, but not the static types of the individual elements. You can tell this from looking at the class file format.

It is theoretically possible that the JIT compiler could use "escape analysis" to eliminate unnecessary type checks in some assignments. However, doing this to the degree you are suggesting would be beyond the bounds of realistic optimization. The payoff of analysing the types of individual elements would be too small.

Besides, people should not write application code like that anyway.

拿命拼未来 2024-08-26 22:21:30

用于在运行时执行转换的字节码指令称为checkcast。您可以使用 javap 反汇编 Java 代码以查看生成了哪些指令。

对于数组,Java 在运行时保留类型信息。大多数时候,编译器会为您捕获类型错误,但在某些情况下,当您尝试将对象存储在数组中时,您会遇到 ArrayStoreException ,但类型不匹配(并且编译器没有捕获它)。 Java 语言规范 给出了以下示例:

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    public static void main(String[] args) {
        ColoredPoint[] cpa = new ColoredPoint[10];
        Point[] pa = cpa;
        System.out.println(pa[1] == null);
        try {
            pa[0] = new Point();
        } catch (ArrayStoreException e) {
            System.out.println(e);
        }
    }
}

Point[] pa = cpa 有效,因为 ColoredPoint 是 Point 的子类,但 pa[0] = new Point() 无效。

这与泛型类型相反,泛型类型在运行时不保存类型信息。编译器会在必要时插入 checkcast 指令。

泛型类型和数组的类型差异使得数组和泛型类型通常不适合混合使用。

The byte code instruction for performing casting at runtime is called checkcast. You can disassemble Java code using javap to see what instructions are generated.

For arrays, Java keeps type information at runtime. Most of the time, the compiler will catch type errors for you, but there are cases where you will run into an ArrayStoreException when trying to store an object in an array, but the type does not match (and the compiler didn't catch it). The Java language spec gives the following example:

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    public static void main(String[] args) {
        ColoredPoint[] cpa = new ColoredPoint[10];
        Point[] pa = cpa;
        System.out.println(pa[1] == null);
        try {
            pa[0] = new Point();
        } catch (ArrayStoreException e) {
            System.out.println(e);
        }
    }
}

Point[] pa = cpa is valid since ColoredPoint is a subclass of Point, but pa[0] = new Point() is not valid.

This is opposed to generic types, where there is no type information kept at runtime. The compiler inserts checkcast instructions where necessary.

This difference in typing for generic types and arrays makes it often unsuitable to mix arrays and generic types.

祁梦 2024-08-26 22:21:30

理论上,会引入开销。
然而,现代 JVM 很智能。
每个实现都是不同的,但是假设可能存在一种 JIT 优化掉强制转换检查的实现,当它可以保证永远不会发生冲突时,这并不是没有道理的。
至于哪些特定的 JVM 提供了这个功能,我无法告诉你。我必须承认我自己很想了解 JIT 优化的具体细节,但这些都是 JVM 工程师需要担心的。

这个故事的寓意是首先编写可理解的代码。如果您遇到速度缓慢的情况,请分析并确定您的问题。
这很可能不是由于选角造成的。
永远不要为了优化代码而牺牲干净、安全的代码,除非您知道需要这么做。

In theory, there is overhead introduced.
However, modern JVMs are smart.
Each implementation is different, but it is not unreasonable to assume that there could exist an implementation that JIT optimized away casting checks when it could guarantee that there would never be a conflict.
As for which specific JVMs offer this, I couldn't tell you. I must admit I'd like to know the specifics of JIT optimization myself, but these are for JVM engineers to worry about.

The moral of the story is to write understandable code first. If you're experiencing slowdowns, profile and identify your problem.
Odds are good that it won't be due to casting.
Never sacrifice clean, safe code in an attempt to optimize it UNTIL YOU KNOW YOU NEED TO.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文