如何编写代码来帮助垃圾收集器

How to write code to help garbage collector

面试时我遇到了一个问题 - "How to help garbage collector when writing code?"。

在我看来,GC 工作得很好,除了基本的良好代码实践之外,我们不需要使用任何东西,后来补充说,在 finally 子句中关闭资源肯定有助于 GC,但通常对这样的问题感到惊讶。

有好的回复吗?我们需要做一些不可思议的事情来帮助垃圾收集器吗?

垃圾收集器的工作非常高效灵巧,但我们可以通过将变量和对象指向 null 来增强其收集垃圾的能力,这些变量和对象不再被使用,也不再有对它们的引用。 在文件处理期间使用 close()flush() 方法。 并在线程处理中销毁不使用的线程。

垃圾收集器 "task" 定期扫描您的对象以检测未使用的对象(实际上它是一种模拟具有无限内存的机器的机制)。

因此,当您持有对不再需要的对象实例的引用时,必须将其设置为 null 以便让垃圾收集器知道您不再对此感兴趣实例(可能还有它的后代)。

这并不意味着你必须在使用后将每个变量都设置为null,但你必须注意字段。 由于 Java 没有处置模式(有关更多信息,请参阅 here),您必须设计 API 来模仿它:当您使用完一个包含要释放的引用的对象实例时,您必须添加适当的方法来执行此类操作。

考虑以下示例:

class MyClass1
{
    int Field1;
    int Field2;
    int Field3;
    int Field4;
}

class MyClass2
{
    private MyClass1 m_MyReference;

    public MyClass2()
    {
        m_MyReference = new MyClass1();
    }

    public void DoSomething()
    {
        // do something here that uses m_MyReference.
    }
}

如果保留了 MyClass2 的一个实例,但程序的其他一些可访问实例(例如,单例或当前在堆栈中的某个实例),您将永远不会释放与 MyClass1 关联的内存,因为它仍然被 m_MyReference.

这很糟糕吗?这取决于 MyClass2 和 MyClass1 是否真的在做。

如果您知道 MyClass2 可能有很长的生命周期并且您希望保留对 MyClass2 的引用但要释放与 MyClass1 关联的内存,则必须执行类似于以下代码的操作:

class MyClass2
{
    private MyClass1 m_MyReference;

    public MyClass2()
    {
        m_MyReference = new MyClass1();
    }

    public void DoSomething()
    {
        // do something here that uses m_MyReference.
    }

    public void Dispose()
    {
        m_MyReference = null;
    }

}

通过这种方式,您可以向调用者公开一种方式,表明您不再持有对您不需要的实例的引用。

请记住,简单地将 null 分配给变量或字段不会自动释放内存。垃圾收集器是异步的,并在它决定时运行。 希望能在不深入的情况下给出想法。

这个问题的措辞很奇怪。 GC 的工作不需要帮助。它会在任何强加的约束条件下工作,如果不能满足约束条件,它就会失败。

当然可以更改工作,但这并不是出于减轻 GC 负担的愿望——它不是一个会厌倦其工作的人——而是出于某种不可告人的目的,例如提高整体程序性能。

这些事情通常被定义为延迟、能耗、吞吐量或内存占用优化。但这些并不是程序员关心的唯一指标。简单性和代码可读性也很重要。

朴素代码可读性更高但性能更差。如果您的目标是 easy-to-read 代码,那么执行减少 GC 负载的复杂优化可能是 counter-productive,因此 "helping the GC" 本身并不是目标。

现在,如果您的目标是提高某些性能指标,那么一些优化还涉及编写代码来减少内存管理(分配 + GC)子系统所做的工作。一些可能的优化包括避免终结器、发现内存泄漏、减少不必要的分配、避免庞大的 Object[] 数组、调整 GC 参数、购买更好的硬件和缩短对象的生命周期。哪种优化适用取决于应用程序,最好通过分析器、GC 日志记录和相关

来确定