【问题标题】:What destroys the local variable in java?什么破坏了java中的局部变量?
【发布时间】:2019-09-15 21:10:35
【问题描述】:

我怀疑方法局部变量仅在方法执行时才存在。另外,当EdenLong-generation块溢出(次要/主要GC)等时触发GC......那么,如果在方法体的末尾Eden没有溢出,所以没有必要触发GC。尽管没有触发主要/次要 GC,我们将销毁所有局部变量。是怎么做到的?

【问题讨论】:

    标签: java garbage-collection


    【解决方案1】:

    方法局部变量(或通常称为“局部变量”)分配在每个线程的堆栈上。变量本身不受垃圾收集的影响。当方法调用(正常或异常)终止时会自动回收它们1

    对象是另一回事。对象(包括数组)通常2 分配在堆上,它们会受到垃圾回收。


    那么通过方法分配并分配给局部变量的对象(或数组)呢?

    首先,局部变量持有对对象的引用。对象存储在堆中(见上文)。

    当垃圾收集器运行时(您通常不知道什么时候会运行!),它将检查任何现有的局部变量以查找仍在进行中的方法调用。如果变量包含对对象的引用,则将这些对象添加到要保留的对象列表中……并检查它们是否引用其他对象,等等。


    因此,总而言之,局部变量在方法调用结束时会自动销毁,但这些变量引用的对象将继续存在,直到 GC(最终)发现它们无法访问。


    1 - 我们需要考虑从内部类或在变量范围内声明的 lambda 访问的局部变量。 lambda 实例的类可能会传递到某个地方,以便在方法返回后可以使用它。在这种情况下,您会认为局部变量需要在方法返回后存在。实际上,发生的情况是局部变量被复制到表示内部类或 lambda 实例的对象中的合成字段。然后类 / lambda 使用该字段中的值。原始变量确实在其方法终止时消失。

    2 - 最近的 Hotspot JIT 编译器有一个称为“转义分析”的可选优化,用于查找可以在线程堆栈上分配由方法调用创建的对象的情况。默认情况下不启用此功能。如果一个对象是在栈上分配的,那么当方法调用结束时它会被回收。 GC不参与。

    3 - 你说:“当 Eden 或 Long-generation 块溢出(次要/主要 GC)等时触发 GC...”。不一定如此。一些低暂停收集器在相应的空间填满之前被触发。但是,这不会改变上述任何内容。

    【讨论】:

      【解决方案2】:

      垃圾收集器——有时也被称为死神——按照自己的时间表运行,并收集超出参考的对象。 当然,局部变量在方法退出后不能被引用,因为它们超出了范围,所以对你的程序来说它们是死的*,但它们仍然存在于堆上,直到 GC 运行。

      在正常情况下(和大多数异常情况),你不需要告诉死神什么时候做它的工作。它会在需要的时候悄无声息地来,带走那些不再需要的东西。这是使用高级语言工作的主要优势之一:可以安全地假设您永远不需要考虑管理死对象的释放等事情。你可以把它们扔在你的肩膀上,知道它们永远不会打扰你。我想有一些高性能、高需求的应用程序需要摆弄 GC,但这是一种优化,除非你有非常好的相反证据,否则应该总是认为它为时过早。

      *当然,返回给调用函数的局部变量除外,它们可能成为该范围内的局部变量并获得更多的生命。一般来说,规则是:如果你的代码的某些部分仍然关心变量,它不会被垃圾回收,如果你的程序没有任何部分关心它,那么你不需要考虑它。

      【讨论】:

      • 它在 Java 中从未被称为“收割者”。或者至少,不是来自任何可靠/可信的来源。
      • @StephenC 我不认为这很重要。至少,我明白乔恩想说什么。
      • 是的。我。我是源头。你有反例吗? (除了你自己。)
      • 我同意斯蒂芬的观点,我以前从未听过有人称它为收割者。
      • "局部变量当然不能在方法退出后被引用[...]" 如果它们被相关方法返回怎么办?
      【解决方案3】:

      Java 虚拟机使用线性堆栈来管理(存储/访问)局部变量和方法参数,因此它只是将堆栈指针设置为没有声明任何局部变量和参数并继续前进的点。不用GC了

      【讨论】:

      • 有点不清楚。 GC如何区分存储在Eden中的本地/非本地变量?
      • @St.Antario GC 不关心本地或非本地。它关心主动引用。如果一个变量没有活动引用,那么没有任何东西可以引用它,所以它可以被回收。当一个方法退出时,任何局部变量都会失去它们的局部引用,所以除非你将它们传递给其他挂在它们上面的对象,否则它们将进入天空中的大桶。
      • @JonKiparsky 因此,我们可以重用未被任何活动引用引用的内存。如果是局部变量,在函数体完成后,我们可以将一个新的对象放到存放局部变量的地方,对吧?
      • @St.Antario 好吧,关键是在 Java 中,您永远不需要考虑将对象放在哪里。机器分配内存,机器回收内存,您可以放心地假设,只要您不尝试分配比 VM 更多的内存,它就会找到一种方法让这一切正常工作。同样,这是使用高级语言的好处之一。您不必考虑堆的状态。
      • @St.Antario 但是是的,你是对的:当一个函数的主体完成时,只要该函数尚未通过,不久之后该函数中分配的任何内存都将可用对该位置中的对象的引用,指向持有它的东西。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-30
      相关资源
      最近更新 更多