这种形式的内存管理在 Java 中有意义吗?
在我的应用程序的生命周期中,我必须重新创建一个包含其他对象 ArrayList 的 ArrayList(从存储中读取它们)。 ArrayList 始终分配给类中的相同数据成员,本质上使旧的 ArrayList-of-ArrayList 悬空(不可引用或不可访问)。
我的理解是,与 C++ 不同,这不会导致内存泄漏,因为系统知道对象何时变得不可引用,并在它认为合适时释放内存。
尽管如此,旧习惯很难克服,我几乎本能地想通过在重新创建 ArrayList-of-ArrayLists 之前自己做一些清理来“帮助”系统:
for (InnerArray aList : outerArray)
aList.clear();
outerArray.clear();
我的问题:
- 这是多余的吗?
- 我是不是弊大于利?
- 这有什么好处吗?
- 为什么?
In the life-cycle of my application, I have to re-create an ArrayList that contains other ArrayLists of objects (reading them from storage). The ArrayList is always assigned to the same data member in a class, essentially leaving the older ArrayList-of-ArrayLists dangling (unreference-able or inaccessible).
My understanding is that, unlike in C++, this doesn't result in a memory leak because the system knows when an object becomes unreference-able and frees the memory when/as it sees fit.
Still, old habits are hard to overcome and I am almost instinctively drawn to "help" the system by doing some cleanup myself before re-creating that ArrayList-of-ArrayLists:
for (InnerArray aList : outerArray)
aList.clear();
outerArray.clear();
My questions:
- Is this redundant?
- Am I doing more harm than good?
- Is this good in any way?
- Why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是多余的。您不会帮助释放这些对象,因为垃圾收集已经确定它们无法访问。你不能让他们变得比现在更遥不可及。因此,您在毫无效果的事情上浪费了一点处理时间。
不过,主要问题是您让其他开发人员感到困惑,他们会想知道为什么要清除该数组。如果有一些他们一开始没有看到的隐藏原因,他们可能会浪费时间试图弄清楚你为什么这样做。
C++ 开发人员在使用 Java 代码时往往会遇到的一个类似问题是制作 终结器;在 Java 中,您几乎不想这样做,因为终结器会增加垃圾收集的额外成本,并且它们不是释放资源的可靠方法,因为它们可能很长一段时间或永远不会被调用。
It is redundant. You are not helping those objects get freed, because garbage collection will already determine them to be unreachable. You can't make them more unreachable than they already are. So, you waste a little bit of processing time on something that accomplishes nothing.
The main problem, though, is that you are confusing other developers who will be wondering why you're clearing that array. They may waste their time trying to figure out why you were doing that, if there's some hidden reason that they didn't see at first.
A similar problem that C++ developers tend to have with Java code is making finalizers; in Java you almost never want to do this, as finalizers add an additional cost to garbage collection and they aren't a reliable way to free resources because they may not get called for a long time, or ever.
PS 我不确定我现在要说什么,但是
clear()
函数可能会从ArrayList
中删除元素code> 只需确保您无法再通过 ArrayList 变量访问它们即可。也许它不会“摧毁”它们,但只会使它们有资格进行垃圾收集。如果知道该函数是如何实现的那就太好了...PPS 您可能会想自己调用 GC,但不要这样做!请参阅相关问题。
编辑:
这是来自
ArrayList
的clear()
:P.S. I'm not sure about what I'm going to say now, but the
clear()
function might remove elements from anArrayList
just by making sure that you cannot access them anymore through theArrayList
variable. Probably it won't 'destroy' them but it will just make them eligible for garbage collection. It would be nice to know how that function is implemented...P.P.S. You might be tempted to invoke GC yourself, don't do it! See related question.
EDIT:
Here is the
clear()
fromArrayList
:是的。
是的。
不。
在
ArrayList
上调用clear()
会更改列表的size
字段,然后小心地将null
分配给其元素刚刚变得无效的支持数组。clear()
的代码需要执行此操作,因为这些元素中的引用可能会导致其他对象保持可访问性;即泄漏内存。但是,当
ArrayList
变得无法访问时,垃圾收集器将不再查看后备数组的任何元素。 GC的标记过程只遍历可达对象,回收过程不查看垃圾对象/数组的内容。因此,基本上,对即将变得无法访问的对象调用
clear()
完全是对 CPU 周期和内存/VM 带宽的浪费。 (当然,这是多余的代码,下一个人会摸不着头脑......)事实上,调用
clear
然后重用列表是否是一个好主意甚至是有争议的all:clear
不会释放/调整后备数组的大小,因此对于通常尺寸较小的列表,您可能会留下一个巨大的后备数组。垃圾收集器可以比
clear()
完成其工作更快地释放足够大的数组并分配一个相同大小的新数组,这并非不可想象。 (首先,GC 可以使用多个处理器,其中对clear()
的调用将在一个线程上运行。)绝对有必要清除/重用列表的唯一情况是当其他东西引用该特定列表并且您无法更新列表引用时。
在内存受限和/或 GC 缓慢的平台上清除/重用列表也可能有好处。但是,除非我有强有力的证据表明这是一个问题,否则我不会尝试这种“优化”。 (这种优化会使您的代码变得更加复杂,并且如果做得不正确,可能会导致其本身的性能问题。)
Yes.
Yes.
No.
Calling
clear()
on anArrayList
changes the list'ssize
field and then carefully assignsnull
to the elements of its backing array that that have just become invalid. The code ofclear()
needs to do this because the references in those elements could cause other objects to remain reachable; i.e. leak memory.However, when an
ArrayList
becomes unreachable, the garbage collector won't ever again look at any of the elements of the backing array. The GC's marking process only traverses the reachable objects, and the reclamation process doesn't look at the contents of garbage objects / arrays.So, basically, calling
clear()
on an object that it about to become unreachable is a total waste of CPU cycles and memory / VM bandwidth. (And of course it is redundant code that the next guy is going to scratch his head about ...)In fact, it is even debatable whether calling
clear
and then reusing a list is a good idea at all:Calling
clear
doesn't release / resize the backing array, so you could be left with a huge backing array for a list whose size is usually small.It is not inconceivable that the garbage collector can free a large enough array and allocate a new one of the same size faster than
clear()
can do its job. (For a start, the GC can use multiple processors, where a call toclear()
will run on one thread.)The only scenario where its is absolutely necessary to clear/reuse a list is when something else references that particular list and you can't update the list references.
There may also be benefit in clearing / reusing lists on platform with constrained memory and/or a slow GC. However, I wouldn't attempt this "optimization" unless I had STRONG evidence that this was a problem. (This kind of optimization makes your code more complicated, and can lead to performance problems of its own if it is done incorrectly.)