2010-10-13 96 views
31

我一直在试图了解是什么真正含义是:什么是方法内联?

内联函数

在C++中, 类的声明中定义的成员函数。 (2)函数 调用编译器用 替换该函数的实际代码。 关键字内联可用于暗示 编译器执行内联 扩展的成员体或非成员函数。

直列

要在 编译替换函数调用该函数的代码的副本 。

例如,它被写入这样的:

当一个方法是最后,也可能是 内联。

这里:http://www.roseindia.net/javatutorials/final_methods.shtml

你能给我举一个例子,要么就是根本帮助我了解什么“可以联”的意思。

谢谢。

+1

这将是帮助:http://java.sun.com/developer/onlineTraining/Programming/JDCBook/perf2.html#vm – codaddict 2010-10-13 15:01:58

回答

49

内联是由Java Just-In-Time编译器执行的优化。

如果你有一个方法:

public int addPlusOne(int a, int b) { 
    return a + b + 1; 
} 

,你这样调用:

public void testAddPlusOne() { 
    int v1 = addPlusOne(2, 5); 
    int v2 = addPlusOne(7, 13); 

    // do something with v1, v2 
} 

编译器可能会决定更换你的函数调用的函数体,这样的结果将实际看起来是这样的:

public void testAddPlusOne() { 
    int v1 = 2 + 5 + 1; 
    int v2 = 7 + 13 + 1 

    // do something with v1, v2 
} 

编译器会这样做以节省开销o f实际上正在进行函数调用,这将涉及将每个参数推送到堆栈。

这显然只能用于非虚拟功能。考虑如果方法在子类中被覆盖并且包含该方法的对象的类型在运行时才知道,会发生什么......编译器如何知道要复制的代码:基类的方法体或子类方法体?由于Java中默认所有方法都是虚拟的,因此可以明确标记那些不能被覆盖的方法(或将它们放入final类中)。这将帮助编译器确定该方法永远不会被覆盖,并且内联是安全的。 (请注意,编译器有时也会对非最终方法做出此确定。)

另请注意,单词可能是的引用。最终的方法不保证是可以接受的。有多种方法可以保证方法不能被内联,但没有办法使编译器内联。无论如何,当内联有助于缓解代码的速度时,它几乎总是会比你更清楚。

请参阅wikipedia以获得有关优点和问题的良好概述。

+10

好的答案,但作为一个提示:如果你有一个非最终的方法,你*不*覆盖,一个好的JIT可以计算出来并内联它。如果您然后加载一个覆盖它的类,它可以撤消内联。 – naiad 2010-10-13 15:09:19

+0

好点,我将它添加到答案主体。 – 2010-10-13 15:18:51

+0

这是非常具有说服力的,谢谢你这个伟大的答案。 – Tarik 2010-10-13 15:29:39

9

假设你有一个类,看起来像这样:

public class Demo { 
    public void method() { 
     // call printMessage 
     printMessage(); 
    } 

    public void printMessage() { 
     System.out.println("Hello World"); 
    } 
} 

通过以下方式来printMessage的通话可能被“内联”:

public class Demo { 
    public void method() { 
     // call printMessage 
     System.out.println("Hello World"); // <-- inlined 
    } 

    public void printMessage() { 
     System.out.println("Hello World"); 
    } 
} 

(这实际上是没有这样做的(甚至在字节码级别上),但是在JIT编译期间,但上面的例子说明了内联的概念。)

现在考虑会发生什么如果printMessage方法由另一个类,像这样的超载:

class SubDemo extends Demo { 
    public void printMessage() { 
     System.out.println("Something else"); 
    } 
} 

现在,如果编译器内联调用Demo.printMessage,将与System.out.println("Hello World");卡住这将是错误的情况下,对象竟是SubDemo的实例。

但是,如果方法被宣布为final这在任何情况下都不会是这种情况。如果方法是“最终的”,这意味着它永远不会被新的定义覆盖,因此,将它内联是安全的!

+0

也很好的答案,但作为从另一个答案复制的说明:如果你有一个非最终的方法,你不会覆盖,一个好的JIT可以把它解释出来并反过来嵌入它。如果您然后加载一个覆盖它的类,它可以撤消内联。 – naiad 2010-10-13 15:13:08

+0

这是非常具有说服力的,谢谢你这个伟大的答案。 – Tarik 2010-10-13 15:31:59

10

调用函数不是免费的。机器必须保持一个堆栈帧,以便在被调用函数完成时它可以返回到代码的调用部分。维护堆栈(包括在该堆栈上传递函数参数)需要时间。

当函数内联时,编译器用该函数的代码替换对函数的调用,以便在运行时避免函数调用的性能损失。这是编程中经典的权衡之一:运行时代码变得更大(占用更多内存),但运行速度更快。

+0

谢谢。在第一段中,你确实是对的。 – Tarik 2010-10-13 15:31:39