我刚刚接受了一次采访,被要求用Java创建内存泄漏。
不用说,我觉得自己很傻,不知道如何开始创作。
什么样的例子?
我刚刚接受了一次采访,被要求用Java创建内存泄漏。
不用说,我觉得自己很傻,不知道如何开始创作。
什么样的例子?
当前回答
我曾经有过一次关于PermGen和XML解析的“内存泄漏”。我们使用的XML解析器(我记不清是哪一个)对标记名执行String.intern(),以加快比较速度。我们的一位客户有一个好主意,不将数据值存储在XML属性或文本中,而是将其存储为标记名,因此我们有了这样一个文档:
<data>
<1>bla</1>
<2>foo</>
...
</data>
事实上,他们没有使用数字,而是使用更长的文本ID(约20个字符),这些ID是唯一的,每天的使用率为1000万至1000万。这使得每天有200 MB的垃圾,而这些垃圾再也不需要了,也永远不会被GCed(因为它在PermGen中)。我们将permagen设置为512MB,因此内存不足异常(OOME)需要大约两天的时间才能到达。。。
其他回答
我认为,一个有效的例子可能是在线程集中的环境中使用ThreadLocal变量。
例如,使用Servlet中的ThreadLocal变量与其他web组件通信,让容器创建线程,并在池中维护空闲线程。ThreadLocal变量如果没有正确清理,将一直存在,直到同一个web组件覆盖它们的值。
当然,一旦确定,问题很容易解决。
我最近修复的一个示例是创建新的GC和Image对象,但忘记调用dispose()方法。
GC javadoc代码段:
应用程序代码必须显式调用GC.dispose()方法以在以下情况下释放每个实例管理的操作系统资源不再需要这些实例。这一点尤为重要在Windows95和Windows98上可用的设备上下文数。
图像javadoc片段:
应用程序代码必须显式调用Image.dispose()方法在以下情况下释放每个实例管理的操作系统资源不再需要这些实例。
重叠侦听器是内存泄漏的一个很好的例子:对象被添加为侦听器。当不再需要对象时,对象的所有引用都为空。然而,忘记从侦听器列表中删除对象会使对象保持活动状态,甚至对事件做出响应,从而浪费内存和CPU。看见http://www.drdobbs.com/jvm/java-qa/184404011
以在任何servlet容器(Tomcat、Jetty、GlassFish等)中运行的任何web应用程序为例。连续重新部署应用程序10或20次(只需在服务器的autodeploy目录中触摸WAR即可)。
除非有人真的测试过,否则在重新部署几次之后,很可能会出现OutOfMemoryError,因为应用程序没有注意自己的清理。通过此测试,您甚至可以在服务器中发现错误。
问题是,容器的生存期比应用程序的生存期长。您必须确保容器对应用程序的对象或类的所有引用都可以被垃圾收集。
如果只有一个引用在web应用程序的取消部署后仍然存在,则相应的类加载器以及web应用程序中的所有类都不能被垃圾收集。
应用程序启动的线程、ThreadLocal变量、日志附加器是导致类加载器泄漏的常见原因。
我在javax.swing.JPopupMenu中遇到了非常真实的内存泄漏。
我有一个GUI应用程序,它显示多个选项卡式文档。关闭文档后,如果在选项卡上的任何组件上使用了右键单击上下文菜单,它就会在内存中停留。这些菜单在选项卡之间共享,结果是,在调用popupMenu.show(component invoker,int x,int y)后,组件会作为菜单的“调用程序”静静地存在,直到下一次更改或被setInvoker(null)清除。间接地,调用者引用持久化了整个文档以及与之相关的所有内容。
值得注意的是,菜单只能以这种方式保存对旧组件的一个引用,因此这种内存泄漏不会在没有绑定的情况下增长。