如何编写代码来帮助垃圾收集器
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 日志记录和相关
来确定
面试时我遇到了一个问题 - "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 日志记录和相关
来确定