---
final
---

#### 目录

1. final
   - 引言
   - 内存语义
   - 优势所在
     - 天生线程安全
     - 编译器优化
   - 参考

#### 引言

说起 final 关键字，脱口而出就是修饰变量变量不可变、修饰方法方法不可重写、修饰类类不可继承。

或许你还听过 final 能够提高效率，那如何提高效率的呢？方法内联？方法内联又是个什么东西呢？

带着疑问来探索一下 final。

#### 内存语义

##### 写 final 域的重排序规则

写 final 域的重排序规则禁止把 final 域的写重排序到构造函数之外，这个规则的实现包含两个方面：

1. JVM 禁止编译器把 final 域的写重排序到构造函数之外
2. 编译器会在 final 域的写之后，构造函数 return 之前，插入一个 StoreStore 内存屏障，这个屏障禁止处理器把 final 域的写重排序到构造函数之外

写 final 域的重排序规则可以确保：在对象引用为任何线程可见之前，对象的 final 域已经被正确初始化过了，而普通域不具有这个保证。

##### 读 final 域的重排序规则

在一个线程中，初次读对象引用与初次读改对象包含的 final 域，JMM 禁止处理器重排序这两个操作。编译器会在读 final 域操作的前面插入一个 LoadLoad 内存屏障。

读 final 域的重排序可以确保：在读一个对象的 final 域之前，一定会先读包含这个 final 域的对象引用。

#### 优势

##### 线程安全

这个不用多说，不可变对象天生线程安全。

##### 编译器优化

经常能听到别人说 final 能提高效率，在于编译器在对 final 修饰的方法做了方法内联。这句话对与否先不讨论，先来了解一下方法内联是什么鬼？

在了解方法内联之前，先来了解一下函数调用过程：

1. 首先会有个执行栈，存储它们的局部变量、方法名、动态链接地址
2. 当一个方法调用，一个新的栈桢会被加到栈顶，分配的本地变量和参数会存储在这个栈桢
3. 调转到目标方法代码执行
4. 方法返回的时候，本地方法和参数被销毁，栈顶被移除
5. 返回原来的地址执行

其实这就是虚拟机栈执行方法的内存模型。函数调用需要一定的时间开销和空间开销，当一个方法被频繁调用时，这个时间和空间开销会相对变的很大。根据二八原则，80% 的性能消耗其实是发生在 20% 的代码上，对热点代码的针对优化可以提升整体系统的性能。

然后我们在看看方法内联到底是什么？其实可以用以下例子粗鄙的解释：

```java
private int add2(int x1 , int x2 , int x3 , int x4) {
return add1(x1 , x2) + add1(x3,x4);
}
  
private int add1(int x1 , int x2) {
return x1 + x2;
}
```

在运行一段时间后，代码可能就变成下面这样了：

```java
private int add2(int x1 , int x2 , int x3 , int x4) {
//return add1(x1 , x2) + add1(x3,x4);
return x1 + x2 + x3 + x4;
}
```

嘿，看来方法内联就是把调用的方法函数代码复制到了调用函数内，减少因函数调用而带来的开销。

但是方法内联也是有条件的：

1. 热点代码
2. 方法体不能太大

方法内联会导致拷贝代码副本过多，代码占用内存增加，所以方法体不能太大。

其次由于方法可能被继承，导致需要类型检查而没有达到性能的效果，所以想要对热点代码进行方法内联，最好尽量使用 final、private、static 这些修饰方法，避免因为继承而导致额外的类型检查。

说到这，其实就能理解 final 能够提高效率的原因在于哪了，**但是如果想通过 final 来提升性能，那几乎是不太可能。**

现在，让我来回答一下 final 能够提高性能这个说法？

emmmm，这样的说法太片面。final 的确能够提升性能，但毕竟微乎其微。final 修饰的变量值多数情况下就能在编译阶段确定下来，这就可能导致某些代码被编译器优化掉。其次，关于方法内联的说法，也的确存在。但是方法内联也是需要条件的，首先是被频繁调用的热点代码，方法内联能够减少方法调用所带来的性能消耗；其次是方法体不能过大，原因很简单，方法内联将会导致拷贝代码更多，代码占用更多的内存空间。最后才是方法最好能被 final、private、static 修饰，这样就能避免过多的参数检查、类型检查等等。我们更应该关注 final 所带来的特性，比如属性不可变、方法不可重写、类不可继承，想要通过 final 来提升性能，几乎是不可能的事。

#### 参考

[深入理解Java中的final关键字](http://www.importnew.com/7553.html)

[Final of Java，这一篇差不多了](https://www.jianshu.com/p/f68d6ef2dcf0)

[final修饰递归方法会提高效率吗？](https://www.zhihu.com/question/66083114)

[Java 方法内联](https://www.cnblogs.com/xyz-star/p/10152564.html)