【问题标题】:Free heap memory with null then GC使用 null 然后 GC 释放堆内存
【发布时间】:2014-07-12 23:07:26
【问题描述】:

假设我有这个代码:

DataStructure hugeData = Data.readLotsOfStuff(); // like gigabytes
DataStructure processedData = processData(hugeData);
// now I don't need hugeData, so
hugeData = null;
System.gc();

像这样显式释放内存是个好习惯吗?

【问题讨论】:

  • 这被认为是一种不好的做法。如果需要由无法访问的对象保留的内存,则无论如何都会运行垃圾收集器。此外,当它不能保证时(尽最大努力释放无法访问的内存)
  • 当您不再需要引用时将指针设置为 null 是一种很好的做法(尽管很少需要使用局部变量引用,只有类静态成员和实例成员)。显式调用垃圾收集器不是。

标签: java memory memory-management garbage-collection


【解决方案1】:

关心这些事情通常不是一个好习惯。它使代码更难阅读(每增加一行都需要时间阅读),并且在System.gc() 的情况下,所有不良运行时行为的情况都可能发生。它会混淆内部垃圾收集器的预测,通常会触发非常重/慢的 GC。 (特别是如果您的代码用于并发使用的大型应用程序中,那简直就是地狱。

尽早清空局部变量(硬引用)有问题:

a) 如果您的引用仅使用到某个点并且您的代码块要大得多,那么它可能太大了。

b) 最好将局部变量设为 final,如果您知道变量不会改变,您可以更好地阅读代码。无论如何,这些最终变量不能为空。

c) Java(运行时)编译器和解释器越来越智能。让 VM 决定何时释放引用。

说了这么多。如果将变量的作用域/块留在方法中,则不能期望引用的对象变得不可访问。将变量的范围缩小看起来不错,但对于提前释放引用的问题,它并没有多大作用(除非稍后的范围重新使用该插槽)。如果该方法长时间运行(可能是无限循环),则将引用设为空可能会很好。但只有当你引用一个非常大的对象树时,它才能使对象保持活动状态。否则额外的分配和代码是不值得的。

method() {
  if (...) {
    byte[] ref=new byte[1*MB];
  }
  // B
  while(true) { ... }
}

在这个例子中,1MB的大缓冲区不在位置'B'的范围内,但它仍然在运行时java堆栈上(java VM没有范围的概念,ref保存在局部变量表槽中直到方法离开了)。由于该方法不会(很快)终止,因此将其设为空可能是可以接受的。拆分方法是更好的主意。

永远不要调用 System.gc()。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-12
    • 2015-05-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-12
    • 2014-12-20
    • 1970-01-01
    相关资源
    最近更新 更多