美文网首页JVM
Java中的局部变量与GC root

Java中的局部变量与GC root

作者: 肥兔子爱豆畜子 | 来源:发表于2021-06-22 20:45 被阅读0次

    明确GC roots

    我们知道,java中决定一个对象是不是会被回收要看它是不是还被gc root引用,gc root分为以下几种:

    • 局部变量
    • 静态成员变量引用的对象
    • 本地方法栈JNI本地方法引用的对象
    • 方法区中的常量对象引用的对象

    局部变量在Java内存中的存储模型

    对于“局部变量是gc root”这个说法,准确说是当Java虚拟机栈、也就是线程栈,栈中的栈帧内的局部变量表引用了该局部变量,那么被这个局部变量引用的对象就是gc root。这很容易理解,这时候说明栈帧对应的方法正在调用,所以不能回收方法正在使用的局部变量。
    一旦栈帧弹出,意味着方法调用完毕已返回,那么没有局部变量表引用该局部变量了,所以该局部变量对应的对象也就成为了垃圾对象,可以被回收。
    需要注意的一个事实是,栈帧中的局部变量表登记着该栈帧对应方法内使用的局部变量,局部变量表是在编译阶段就确定的,此时也为变量表中的局部变量分配了内存空间。局部变量的内存是分配在栈空间的,同样类型的变量占用的栈空间都是一样大的。局部变量是基本类型则真值就是存储在该变量分配的栈空间。如果是引用类型、比如是个bean,那么局部变量栈空间存储的只是个引用,指向堆空间中的对象的真值。

    我们来看个例子:

    //看起来会无法垃圾回收,产生OOM的写法
    while(true){
      Car lancer = new Lancer();
      System.gc();
    }
    
    • “不断循环生成新的对象,且局部变量lancer与新对象存在强引用关系,且由于循环的关系栈帧方法没返回、栈帧不弹出,所以这些大量对象无法被gc回收,最终上面代码会产生heap out of memory问题。” 果真如此吗?

    假设上面代码是在一个方法内执行,以上看起来会出问题的代码理论上其实并不会产生OOM,原因在于循环体里边每次new Lancer()生成一个对象,然后局部变量Car lancer指向这个对象。但由于局部变量是在编译阶段分配的内存空间,方法执行过程中对应的栈帧中的局部变量表中,只有lancer这一个局部变量。上面程序相当于反复循环生成新的对象给这一个局部变量赋值。当新的循环生成新Lancer对象赋值给lancer变量时,之前生成的Lancer对象就会失去gc root引用,成为可回收对象,将在System.gc()时被垃圾回收。

    也就是说下面这种看起来比较“性能优化”的写法,实际上跟上面的写法作用是一样的。没任何区别。而且从局部变量作用域来说,循环体内使用的局部变量就应该定义在循环体里边。

    //"优化"写法
    Car lancer = null;
    while(true){
      lancer = new Lancer();
      System.gc();
    }
    

    相关文章

      网友评论

        本文标题:Java中的局部变量与GC root

        本文链接:https://www.haomeiwen.com/subject/ydfmyltx.html