如果垃圾回收器运行异常,是否会导致内存泄漏?

5
我开发了一个J2ME网页浏览器应用程序,它运行良好。我正在测试其内存消耗。据我观察,它存在内存泄漏问题,因为内存监视器(无线工具包的)所表示的已使用内存的绿色曲线在每7次浏览请求(即当终端用户在浏览器中从一个页面导航到另一个页面时)后达到最大分配内存(即687768字节),此后垃圾回收器运行并释放已分配的内存。
我的问题是:
  • 当垃圾回收器每7次页面导航自动运行时,这是否意味着存在内存泄漏?
  • 我是否需要手动运行垃圾回收器(System.gc())一次来防止达到最大分配内存?
请指导我,谢谢。

如果这是一个内存泄漏,垃圾回收器就不会释放已分配的内存,对吧? - nnnnnn
如果您知道所需对象列表并且不想生成太多垃圾,可以对这些对象进行池化并重复使用。这将减少GC运行的频率。 - Vikdor
5个回答

3
要确定是否存在内存泄漏,您需要进一步观察。从您的描述来看,即一旦达到最大内存,GC会启动并能够释放内存使应用程序运行,这听起来不像是有泄漏。此外,您不应自己调用GC,因为它只是一种指示,可能会影响底层算法的性能。您应该集中精力研究为什么您的应用程序在如此短的时间内需要如此多的内存。

谢谢Cratylus,实际上每个请求应用程序都会执行多个密码学操作,这些操作值得消耗这么多内存,但是,我该如何释放已分配的内存,因为每次导航到页面时,我需要为特定类别分配固定大小的内存。即我每个请求运行相同的算法。 - mdawaina
你需要增加堆大小,或者尽可能地使算法更节省空间。 - Cratylus
1
@mdawaina -- 在Java中你无法“释放”内存,当没有任何引用时,垃圾回收器会回收内存。但是内存只在垃圾回收周期中回收。 - Hot Licks

1
我的问题是:当垃圾回收器每7次页面导航自动运行时,是否存在内存泄漏?
不一定。也可能是以下原因之一:
- 您的堆大小与您尝试解决的问题规模不匹配,或者 - 您的应用程序正在以高速率生成(可收集的)垃圾。
实际上,根据您提供的数字,我倾向于认为这主要是一个堆大小问题。如果GC运行之间的时间间隔随时间减少,则会指向内存泄漏的证据,但如果平均速率保持稳定,则表明内存使用和回收的速率处于平衡状态;即没有泄漏。
我需要在每个请求中手动运行垃圾回收器(System.gc())一次,以防止达到最大分配内存吗?
不需要。不需要。不需要。

调用System.gc()不能解决内存泄漏问题。如果确实存在内存泄漏,那么调用System.gc()也无法回收泄漏的内存。事实上,你所做的只是让应用程序运行得更慢...除非JVM完全忽略了这个调用。


有直接和间接的证据表明,HotSpot JVM 的默认行为是遵守 System.gc() 调用:

来自Java 7源代码:

./openjdk/hotspot/src/share/vm/runtime/globals.hpp

  product(bool, DisableExplicitGC, false,                                   \
          "Tells whether calling System.gc() does a full GC")               \

其中 false 是该选项的默认值。(请注意,这是代码树中与操作系统/主机无关的部分。)


感谢Stephen提供宝贵的答案,我认为堆栈确实太小了,正如你所说的。 - mdawaina
@HotLicks - 实际上,1)“大多数平台”是夸张的,2)它在所有平台上都被记录为建议性的,3)我已经提到JVM可能会忽略该调用:请重新阅读我回答的最后9个单词。 - Stephen C
重点是,System.GC可能不会使您的应用程序“运行缓慢”,因为它基本上什么也不做。(而“大多数系统”并非夸张之词。) - Hot Licks
@HotLicks - 你有什么证据支持你的说法,即大多数JVM禁用了System.GC吗?我已经添加了证据(包括JVM源代码),表明它们默认情况下不会禁用它。(我承认生产Java安装程序可以和应该禁用它...但那是另一个问题。) - Stephen C
嗯,我自己在几个JVM中工作过。但是那是几个版本之前的事了。我不知道他们何时添加了你提到的GC选项 - 我不记得了。 - Hot Licks
显示剩余2条评论

1
我写了一个库,它会尽力强制进行垃圾回收。如前所述,System.gc()是异步的,本身不会做任何事情。您可以使用这个库来分析您的应用程序,并找出产生过多垃圾的地方。您可以在this article中阅读更多相关信息,其中我详细描述了垃圾回收问题。

0

这是(半)正常的行为。只有当堆大小达到某个阈值时,触发收集周期,才会收集可用(未引用)存储。

您可以通过更加“堆感知”来减少GC周期的频率。例如,许多程序中的常见错误是使用substring解析字符串,不仅解析最左边的单词,而且还通过对右侧进行子字符串操作来缩短剩余的字符串。创建一个新的String以存储单词是无法避免的,但可以轻松避免重复对原始字符串的“尾部”进行子字符串操作。

运行System.GC将一无所获——在大多数平台上,它是一个空操作,因为它经常被滥用。

请注意,在Java之外(除了愚蠢的Android),您不能真正地出现“内存泄漏”(除非存在严重的JVM错误)。在Java中通常所说的“泄漏”是指未能删除所有对永远不会再次使用的对象的引用。例如,您可能会不断将数据放入链中,并且从不清除对链末端的东西的指针,而这些东西将不再使用。结果的症状是每个周期后使用的最小堆(即GC运行后的大小)不断上升。


0

除了其他出色的答案之外,还要补充一点:

看起来你把内存泄漏垃圾回收混淆了。

内存泄漏是指未使用的内存无法进行垃圾回收,因为它仍然在某个地方有引用(尽管它们没有被用于任何事情)。

垃圾回收是指软件(垃圾收集器)自动释放未引用的内存。

您不应手动调用垃圾收集器,因为这会影响其性能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接