写点什么

JVM 详解之: 汇编角度理解本地变量的生命周期

发布于: 2020 年 07 月 29 日
JVM详解之:汇编角度理解本地变量的生命周期

简介

java 方法中定义的变量,它的生命周期是什么样的呢?是不是一定要等到方法结束,这个创建的对象才会被回收呢?


带着这个问题我们来看一下今天的这篇文章。


本地变量的生命周期

在类中,变量类型有类变量,成员变量和本地变量。


本地变量指的是定义在方法中的变量,如果我们在方法中定义了一个变量,那么这个变量的生命周期是怎么样的呢?


举个例子:


public void test(){    Object object = new Object();    doSomeThingElse(){        ...    }}
复制代码


在上面的 test 方法中,定义了一个 object 本地变量,然后又执行了一个方法。


因为在 java 中,我们无法直接控制对象的生命周期,对象的回收是由垃圾回收器自动进行的。


通常来说这个 object 对象会维持到整个 test 执行结束才会被回收。


现在我们考虑一个特殊的情况,如果 doSomeThingElse 这个方法是一个 while 循环,并且永远不会结束,那么这个创建出来的 object 对象会不会被回收呢?还是一直都存在内存中?


先说我们的结论,JVM 非常智能,可以检测出来这种情况,将 object 对象进行回收。


举例说明

为了能够更好的说明问题,我们自定义一个 Test 对象,并在其创建和被回收之前打印相应的信息。


    public static class Test {       public Test() {           System.out.println("创建对象 " + this);       }
public void test() { System.out.println("测试对象 " + this); }
@Override protected void finalize() throws Throwable { System.out.println("回收对象 " + this); } }

复制代码


然后做两个测试,第一个测试没有无限循环,第二个测试保持无限循环,循环通过一个 volatile 变量 flag 来控制:


public static void main(String[] args) throws InterruptedException {        System.out.println("开始测试1");        resetFlag();        flag = true;        testLocalVariable();
System.out.println("等待Test1结束"); Thread.sleep(10000);
System.out.println("开始测试2"); flag = true; testLocalVariable(); }
复制代码


看一下 testLocalVariable 方法的定义:


    public static void testLocalVariable() {        Test test1 = new Test();        Test test2 = new Test();        while (flag) {            // 啥都不做        }        test1.test();    }
复制代码


然后我们再启动一个线程做定时的 GC。好了一切就绪,我们运行吧:


开始测试1创建对象 com.flydean.LocalVariableReachability$Test@119d7047创建对象 com.flydean.LocalVariableReachability$Test@776ec8df回收对象 com.flydean.LocalVariableReachability$Test@776ec8df测试对象 com.flydean.LocalVariableReachability$Test@119d7047等待Test1结束回收对象 com.flydean.LocalVariableReachability$Test@119d7047
开始测试2创建对象 com.flydean.LocalVariableReachability$Test@4eec7777创建对象 com.flydean.LocalVariableReachability$Test@3b07d329回收对象 com.flydean.LocalVariableReachability$Test@3b07d329
复制代码


先看测试 1 的结果,我们可以看到第二个对象在调用 test1.test()之前就被回收了。


再看测试 2 的结果,我们可以看到第二个对象同样被回收了。


结果说明了 JVM 是足够智能的,可以自行优化本地变量的生命周期。


优化的原因

我们考虑一下,JVM 是在什么阶段对本地变量的生命周期进行优化的呢?


很明显,这个优化不是在编译期间进行的,而是在运行期中进行的优化。


我们使用-XX:+PrintAssembly 分析一下汇编代码:



首先说明,本人的汇编语言还是很多年前学过的,如果解释起来有错误的地方,请多多指正。


先说两个概念 rbx 和 r10 都是 64 位 CPU 的寄存器,r10d 是 r10 的低 32 位。


先看红框 1, 红框 1 表示 rbx 中保存的是我们定义的 LocalVariableReachability 类中的一个 Test 对象。


再看红框 2,红框 2 表示 r10 现在保存的是 LocalVariableReachability 这个类实例。


红框 3 表示的是进入 while 循环的时候,ImutableOopMap 中存储的对象,大家可以看到里面只有 r10 和 rbx,也就是说只有类实例和其中的一个 Test 实例。


红框 4 是什么呢?红框 4 表示的是一个 safe point,也就是垃圾回收的时候的安全点。在这个安全点上如果有不再被使用的对象就会被回收。


因为 ImutableOopMap 中只存有两个对象,那么剩下的一个 Test 实例就会被回收。


总结

本文介绍了本地变量的生命周期,并在汇编语言的角度对其进行了解释,如有错误欢迎指正。


本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/jvm-local-variable-reachability/

本文来源:flydean 的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!


发布于: 2020 年 07 月 29 日阅读数: 60
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
JVM详解之:汇编角度理解本地变量的生命周期