Tom*_*sky 64
内联是由Java Just-In-Time编译器执行的优化.
如果你有一个方法:
public int addPlusOne(int a, int b) {
return a + b + 1;
}
Run Code Online (Sandbox Code Playgroud)
你这样称呼:
public void testAddPlusOne() {
int v1 = addPlusOne(2, 5);
int v2 = addPlusOne(7, 13);
// do something with v1, v2
}
Run Code Online (Sandbox Code Playgroud)
编译器可能决定用函数体替换你的函数调用,所以结果实际上如下所示:
public void testAddPlusOne() {
int v1 = 2 + 5 + 1;
int v2 = 7 + 13 + 1
// do something with v1, v2
}
Run Code Online (Sandbox Code Playgroud)
编译器这样做是为了节省实际进行函数调用的开销,这将涉及将每个参数推送到堆栈.
这显然只能用于非虚函数.考虑如果方法在子类中被覆盖会发生什么,并且直到运行时才知道包含该方法的对象的类型...编译器将如何知道要复制的代码:基类的方法体或子类的方法体?由于默认情况下所有方法在Java中都是虚拟的,因此您可以明确标记那些不能被覆盖的方法final(或将它们放入final类中).这将有助于编译器确定该方法永远不会被覆盖,并且内联是安全的.(请注意,编译器有时也可以对非最终方法进行此确定.)
另外,请在引用中注明单词may.最终方法不保证可以内联.有多种方法可以保证方法无法内联,但无法强制编译器内联.无论如何,当内联将有助于破坏最终代码的速度时,它几乎总会比你更清楚.
有关优点和问题的详细信息,请参阅维基百科.
Chr*_*dge 11
调用函数不是免费的.机器必须保持堆栈帧,以便在被调用函数完成时它可以返回到代码的调用部分.维护堆栈(包括在此堆栈上传递函数参数)需要时间.
当函数内联时,编译器用函数的代码替换对函数的调用,这样就可以避免在运行时调用函数调用的性能损失.这是编程中经典的权衡之一:运行时代码变得更大(占用更多内存),但运行速度更快.
aio*_*obe 10
假设你有一个看起来像这样的类:
public class Demo {
public void method() {
// call printMessage
printMessage();
}
public void printMessage() {
System.out.println("Hello World");
}
}
Run Code Online (Sandbox Code Playgroud)
printMessage可以通过以下方式"内联" 调用:
public class Demo {
public void method() {
// call printMessage
System.out.println("Hello World"); // <-- inlined
}
public void printMessage() {
System.out.println("Hello World");
}
}
Run Code Online (Sandbox Code Playgroud)
(这实际上不是在Java级别上完成的(甚至不是在字节码级别上),而是在JIT编译期间,但上面的示例说明了内联的概念.)
现在考虑如果该printMessage方法被另一个类重载会发生什么,如下所示:
class SubDemo extends Demo {
public void printMessage() {
System.out.println("Something else");
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果编译器内联调用Demo.printMessage它将被卡住,如果该对象实际上是一个实例,System.out.println("Hello World");那将是错误的SubDemo.
但是,如果宣布final该方法,则在任何情况下都不会出现这种情况.如果方法是"最终",则意味着它永远不会被新定义覆盖,因此,内联它是安全的!