【问题标题】:Does variable = null set it for garbage collectionvariable = null 是否将其设置为垃圾收集
【发布时间】:2011-02-25 05:56:15
【问题描述】:

帮我解决与同事的纠纷: 在 Java 中将变量或集合设置为 null 是否有助于垃圾收集和减少内存使用?如果我有一个长时间运行的程序并且每个函数都可能被迭代调用(可能数千次):在将值返回给父函数之前将其中的所有变量设置为 null 是否有助于减少堆大小/内存使用?

【问题讨论】:

    标签: java performance memory memory-management


    【解决方案1】:

    这是旧的表演知识。早在 1.0 天就是这样,但是编译器和 JVM 已经过改进以消除这种需要(如果有的话)。如果您有兴趣,这篇出色的 IBM 文章会详细介绍:Java theory and practice: Garbage collection and performance

    【讨论】:

    • 我怀疑这是真的。我从 1.1.2 开始,那时不是这样。我认为这更像是人们第一次习惯了主流语言中的垃圾收集(?),并有 C++ 习惯。
    【解决方案2】:

    来自文章:

    在一种情况下,使用显式空值不仅有帮助,而且实际上是必需的,那就是对对象的引用范围比程序规范使用或认为有效的范围更广。这包括使用静态或实例字段来存储对临时缓冲区而不是局部变量的引用,或使用数组来存储运行时可以访问但程序的隐含语义无法访问的引用等情况。

    翻译:不再需要的“显式空”持久对象。 (如果你愿意的话。“实际上需要”的声明太强了?)

    【讨论】:

    • 您可以包含对您引用的文章的引用
    • 这是在接受的答案中链接的文章,现在似乎已经消失了:(
    【解决方案3】:

    Java 虚拟机规范

    12.6.1 实现终结 每个对象都可以用两个属性来表征:它可能是可达的、终结器可达的或不可达的,它也可能是未终结的、可终结的或已终结的。

    可达对象是可以在任何潜在的持续计算中从任何活动线程访问的任何对象。可以设计优化程序的转换,将可到达的对象的数量减少到比那些天真地认为是可到达的要少。例如,编译器或代码生成器可能会选择将不再使用的变量或参数设置为 null,以使此类对象的存储空间可能更快地被回收。

    讨论

    如果对象字段中的值存储在寄存器中,则会发生这种情况的另一个示例。然后程序可能会访问寄存器而不是对象,并且永远不会再次访问对象。这意味着该对象是垃圾。

    如果对象可以参与任何潜在的持续计算,则该对象是可达的。因此,如果您的代码引用了一个局部变量,而没有其他内容引用它,那么您可能会通过将其设置为 null 来收集该对象。这要么给出一个空指针异常,要么改变你的程序的行为,或者如果两者都没有,你一开始就不需要这个变量。

    如果你要清空一个字段或数组元素,那么这可能对某些应用程序有意义,它会导致内存被更快地回收。一旦案例创建一个大数组来替换类中的字段引用的现有数组 - 如果在创建替换之前该字段为空,那么它可能会减轻内存压力。

    Java 另一个有趣的特性是作用域不会出现在类文件中,因此作用域与可达性无关;这两种方法创建相同的字节码,因此虚拟机根本看不到创建对象的范围:

    static void withBlock () {
        int x = 1;
    
        {
            Object a = new Object();
        }
    
        System.out.println(x+1);
    }
    
    static void withoutBlock () {
        int x = 1;
    
        Object a = new Object();
    
        System.out.println(x+1);
    }
    

    【讨论】:

      【解决方案4】:

      不一定。当不再有活动线程持有对该对象的引用时,该对象就有资格进行垃圾回收。

      当方法返回时,局部变量超出范围,将局部变量设置为 null 完全没有意义 - 变量无论如何都会消失,如果没有其他东西可以引用变量引用的对象,那么这些对象有资格进行垃圾回收。

      关键不只是查看变量,而是查看这些变量所引用的对象,并找出程序引用这些对象的位置。

      【讨论】:

        【解决方案5】:

        它对局部变量没有用,但它可能有用/需要清除不再需要的实例变量(例如初始化后)。

        (是的,我知道如何应用 Builder 模式...)

        【讨论】:

        • 可以在局部变量上很有用,如果编译器不能解决它自己的变量将不会被再次读取。在大多数情况下,编译器可以自行解决。
        • @IanRingrose 问题是关于在程序退出之前清空。实践是没有用的。编译器不参与垃圾回收。
        【解决方案6】:

        这只能在某些情况下有意义:

        public void myHeavyMethod() {
          List hugeList = loadHugeListOfStuff();  // lots of memory used
          ResultX res = processHugeList(hugeList); // compute some result or summary 
          // hugeList = null;  // we are done with hugeList
            ...
          // do a lot of other things that takes a LOT of time (seconds?)
          // and which do not require hugeList
           ...
        }
        

        这里 可以取消注释 hugeList = null 行,我猜。

        但是重写方法肯定更有意义(也许重构为两个, 或指定内部范围)。

        【讨论】:

        • 仅当 VM 未实现 JVM 规范的第 3 版时。
        • 阅读我的回答中关于可达性的引用。如果 processHughList 不存储对 hugeList 引用的对象的引用,则它不能“在任何活动线程的任何潜在持续计算中访问”,因此无法访问,因此有资格进行垃圾收集。如果 processHughList 只使用大小和数据数组(假设 List 类似于 ArrayList)并且这些被 JITted 作为寄存器变量,那么对象甚至可以在 processHughList 返回之前被收集。
        • 仅当代码过于复杂时编译器/jit 才能看到在调用 processHugeList 后未使用 hugeList。
        【解决方案7】:

        将对象引用设置为 null 只会使其有资格进行垃圾回收。 它不一定释放内存,这取决于垃圾收集器何时运行(取决于JVM)。 当垃圾收集器运行时,它通过仅删除符合垃圾收集条件的对象来释放堆。

        【讨论】:

          【解决方案8】:

          拥有它是一件好事。当您将对象设置为 null 时,可能会在立即 GC 周期中更快地对对象进行垃圾收集。但是没有保证机制可以让对象在给定时间被垃圾回收。

          【讨论】:

          • 在某些特殊情况下这可能是个好主意,但你当然不应该总是将变量设置为null(注意,精确很重要:你不能“将对象设置为空” ) 出于习惯,不假思索。这导致了一种迷信的编程,你不知道自己为什么要这样做,这是一个坏主意。
          • 问题是return前清空,浪费时间。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-17
          • 2017-03-13
          • 2016-08-13
          • 1970-01-01
          • 2010-11-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多