我知道在这段代码中:
public static void main(String[] args) {myMethod();}
private Object myMethod() {
Object o = new Object();
return o;
}
垃圾收集器会在执行o
后销毁myMethod
,因为myMethod
的返回值未分配,因此没有对它的引用。但是如果代码是这样的话会怎么样:
public static void main(String[] args) {myMethod();}
private Object myMethod() {
int i = 5;
return i + 10;
}
编译器是否会麻烦处理i + 10
,因为没有分配返回值?
如果i
不是简单的原语,而是更大的对象:
public static void main(String[] args) {myMethod();}
private Object myMethod() {
return new LargeObject();
}
其中LargeObject
有一个昂贵的构造函数,编译器是否仍会分配内存并调用构造函数,以防它有任何副作用?
如果返回表达式很复杂但没有副作用,这一点尤其重要,例如:
public static void main(String[] args) {
List<Integer> list = new LinkedList();
getMiddle();
}
private Object getMiddle(List list) {
return list.get((int) list(size) / 2);
}
在不使用返回值的情况下在现实生活中调用此方法将毫无意义,但这只是为了举例。
我的问题是:给定这些示例(对象构造函数,对基元的操作,没有副作用的方法调用),如果编译器看到该值未被赋值给它,则可以跳过方法的return语句什么?
我知道我可以针对这些问题进行多次测试,但我不知道我是否会信任他们。我对代码优化和GC的理解是相当基础的,但我想我已经足够了解,特定代码位的处理并不一定是可以推广的。这就是我要问的原因。
答案 0 :(得分:2)
首先,让我们来处理你的问题和一些评论中明显的误解。
在HotSpot(Oracle或OpenJDK)Java平台中,实际上有两个必须考虑的编译器:
javac
编译器将Java源代码转换为字节码。它做了最小化的优化。事实上,它所做的唯一重要的优化是评估编译时常量表达式(这实际上是某些编译时检查所必需的)和重写字符串连接序列。
您可以轻松查看已完成的优化...使用javap
...但它也会产生误导,因为重负荷优化尚未完成。基本上,javap
输出在优化方面几乎没有帮助。
JIT编译器执行重量级优化。它在程序运行时在运行时调用。
不会立即调用它。通常,您的字节码最初几次被解释为调用任何方法。 JVM正在收集行为统计数据,JIT编译器将使用它来优化(!)。
因此,在您的示例中,main
方法被调用一次,myMethd
被调用一次。 JIT编译器甚至无法运行,因此实际上将解释字节码。 但这很酷。 JIT编译器优化的时间比通过运行优化器节省的时间要多。
但假设优化器确实运行了......
JIT代码编译器通常有几个策略:
所以可能会发生什么。
然后您的myMethod()
被优化为一种独立的方法,不会对不必要的语句进行优化。因为在所有可能的情境中他们都没有必要。
当/如果对myMethod()
的方法调用内联时(例如,在main(...)
方法中,优化器将确定(例如)这些语句
int i = 5;
return i + 10;
在此上下文中是不必要的,并将其优化。
但请记住,JIT编译器一直在不断发展。因此,准确地预测将发生什么样的优化,以及何时,几乎不可能。也许没有结果。
建议:
值得思考你是否在&#34;粗略的&#34;水平。选择正确的算法或数据结构通常很关键。
在细粒度级别,通常不值得。让JIT编译器处理它。
除非您有明确的证据表明您需要进行优化(即客观上太慢的基准测试),并明确证明特定点存在性能瓶颈(例如,分析结果)。
答案 1 :(得分:1)
诸如“编译器会做什么?”之类的问题。关于Java有点天真。首先,有两个编译器和一个解释器。静态编译器执行一些简单的优化,例如或许使用有效的最终操作数优化任何算术表达式。它当然将常量,文字和常量表达式编译成字节码文字。真正的魔法发生在运行时。
我认为没有理由为什么结果计算会被优化掉,除非忽略返回值。忽略返回值很少见,应该更少见。
在运行时,可以在上下文中获得更多信息。对于优化,运行时解释器和编译器动态二重奏可以解决诸如“这部分代码是否值得优化?”之类的问题。如果调用者使用返回值,HotSpot及其同类将不会优化return new Foo();
实例化。但是他们可能会采用不同的方式,可能会在堆栈中抛出属性,甚至在情况允许的情况下抛出寄存器。因此,虽然对象存在于逻辑Java堆上,但它可能存在于物理JVM组件的其他位置。
谁知道具体的优化是否会发生?没有人。但他们或类似的东西,或更神奇的东西,可能会发生。可能HotSpot所执行的优化与我们期望或想象的不同并且更好,当它以其智慧决定采取麻烦进行优化时。
哦,在运行时,HotSpot可能会优先处理以前优化的代码。这是为了维护Java代码的语义。