目录
简介
最常用的接口 List
多个 List 的调用
不一样的 List 调用
总结
简介
上一篇文章我们讲解了 Virtual Call 的定义并举例分析了 Virtual Call 在父类和子类中的优化.
JIT 对类可以进行优化, 那么对于 interface 可不可以做同样的优化么?
一起来看看吧.
最常用的接口 List
List 应该是大家最最常用的接口了, 我想这个大家应该不会反驳.
public interface List<E> extends Collection<E> {
今天我们就拿 List 来做例子, 体验一下 JIT 优化接口的奥秘.
还是上代码, 要分析的代码如下:
- public class TestVirtualListCall {
- public static void main(String[] args) throws InterruptedException {
- List<String> list=new ArrayList<>();
- for (int i = 0; i <10000; i++)
- {
- doWithVMethod(list);
- }
- Thread.sleep(1000);
- }
- public static void doWithVMethod(List<String> list)
- {
- list.add("www.flydean.com");
- }
- }
如果在命令行运行, 大家记得在运行时添加参数 - XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:-Inline
直接看 JIT Watcher 的结果:
我们可以看到 JIT 中先对 ArrayList 的实现类做了一个比较.
然后调用的是 invokeinterface, 但是其本质还是 invokevirtual, 并且我们可以看到这个调用是被优化过了: optimized virtual call.
多个 List 的调用
同样的, 我们可以测试一下多个 list 子类的情况下怎么调用:
- public class TestVirtualListCall2 {
- public static void main(String[] args) throws InterruptedException {
- List<String>[] lists=new List[]{new ArrayList<>(),new LinkedList<>()};
- for (int i = 0; i <10000; i++)
- {
- doWithVMethod(lists[i%2]);
- }
- Thread.sleep(1000);
- }
- public static void doWithVMethod(List<String> list)
- {
- list.add("www.flydean.com");
- }
- }
同样, 使用 JIT Watcher 来运行:
我们可以看到 JIT 做了两次对象类型的比较, 然后对两个 invokeinterface 都做了优化.
结果和我们的父类子类结果是一样的.
不一样的 List 调用
上面我们在做多个 list 调用的时候, 是轮循着来调用的, 如果我们先调用 ArrayList 的方法, 再调用 LinkedList 的方法, 会有什么不同呢?
一起来看看.
- public class TestVirtualListCall3 {
- public static void main(String[] args) throws InterruptedException {
- List<String> list1 = new ArrayList<>();
- List<String> list2 = new LinkedList<>();
- for (int i = 0; i <10000; i++)
- {
- doWithVMethod(list1);
- }
- Thread.sleep(1000);
- for (int i = 0; i < 10000; i++)
- {
- doWithVMethod(list2);
- }
- Thread.sleep(1000);
- }
- public static void doWithVMethod(List<String> list)
- {
- list.add("www.flydean.com");
- }
- }
上面我们先循环 ArrayList, 然后再循环 LinkedList.
看下结果有什么不同:
可以看到, JIT 先比较了 ArrayList, 然后只做了一次方法的优化.
也就是说 LinkedList 的调用是没有进行代码优化的.
上面的结果是在 C2 编译器下, 也就是 level4 的编译水平下解析的.
我们看下如果在 C1 编译器下, 也就是 Level3 编译水平下有什么不同.
可以看到 C1 编译下, 所有的 invokeinterface 都没有进行编译优化, 只有在 C2 编译下, 才会进行优化.
不同的 JVM 版本可能优化方式不一样. 大家可以自行实验.
总结
本文用实例展示了 Virtual Call 在 interface 上面的优化使用.
感兴趣的朋友, 可以一起讨论.
来源: https://www.cnblogs.com/flydean/p/jvm-virtual-call-interface.html