面试官:小伙子先来说一下可能引起 Java 内存泄露的场景吧
但是,如果我们只删除重写的?finalize()?方法,则同一个程序会给出以下响应:
如何预防?
=========
我们应该避免使用终结器
内部字符串
=========
当 Java 7 从?PermGen?转移到?HeapSpace?时,Java 字符串池经历了一次重大变化。但是对于在版本 6 及以下运行的应用程序,我们在处理大字符串时应该更
加注意。
如果我们读取一个巨大的字符串对象,并在该对象上调用?intern()?,那么它将进入字符串池,该池位于 PermGen(永久内存)中,只要我们的应用程序运行,它就会一直留在那里。这会阻塞内存并在我们的应用程序中造成内存泄漏。
JVM 1.6 中这个例子的 PermGen 在 VisualVM 中如下所示:
与此相反,在一个方法中,如果我们只是从文件中读取一个字符串,而不是对其进行内接,那么 PermGen 看起来像:
如何预防?
=========
解决这个问题最简单的方法是升级到最新的 Java 版本,因为从 JavaVersion7 开始,字符串池被移到 HeapSpace
如果要处理大型字符串,请增大 PermGen 空间的大小,以避免任何潜在的 OutOfMemoryError?:
-XX:MaxPermSize=512m
使用 ThreadLocals
==================
ThreadLocal 是一种构造,它使我们能够将状态隔离到特定线程,从而允许我们实现线程安全。
当使用这个结构时,每个线程都将持有一个对其 ThreadLocal 变量副本的隐式引用,并将维护自己的副本,而不是在多个线程之间共享资源,只要线程是活动的。
尽管?ThreadLocal?变量有很多优点,但是它的使用还是有争议的,因为如果使用不当,它们会导致内存泄漏。Joshua Bloch 曾经评论过线程本地用法:
线程池的草率使用与线程局部变量的草率使用可能会导致意外的对象保留,正如在许多地方所指出的那样。但把责任推到线程本地上是没有道理的。
threadlocal 导致内存泄漏
=====================
一旦保持线程不再活动,?threadlocal?就应该被垃圾回收。但是当?threadlocal?与现代应用服务器一起使用时,问题就出现了。
现代应用服务器使用一个线程池来处理请求,而不是创建新的请求(例如 apache tomcat 中的 Executor?)。此外,它们还使用单独的类加载器。
由于应用程序服务器中的线程池遵循线程重用的概念,因此它们永远不会被垃圾回收,而是被重用以服务于另一个请求。
现在,如果任何类创建了一个?ThreadLocal?变量,但没有显式地删除它,那么即使在 web 应用程序停止之后,该对象的副本也将保留在工作线程中,从而防止对象被垃圾回收。
如何预防?
=========
remove()
ThreadLocals.set(null)
try {
threadLocal.set(System.nanoTime());
//... further processing
}
finally {
threadLocal.remove();
}
解决内存泄漏的其他方法
===============
虽然在处理内存泄漏时没有一刀切的解决方案,但是我们可以通过一些方法将这些泄漏最小化。
启用分析
========
Java 探查器是监视和诊断应用程序内存泄漏的工具。它们分析应用程序内部发生的事情—例如,如何分配内存。
使用探查器,我们可以比较不同的方法,并找到可以最佳利用资源的领域。
在本教程我们一直在使用 javavisualvm。请查看我们的 Java 探查器指南,了解不同类型的探查器,如任务控制、JProfiler、YourKit、Java VisualVM 和 Netbeans 探查器。
详细的垃圾收集
===========
通过启用详细的垃圾收集,我们可以跟踪 GC 的详细跟踪。要实现这一点,我们需要在 JVM 配置中添加以下内容:
-verbose:gc
通过添加此参数,我们可以看到 GC 内部发生的详细情况:
使用引用对象以避免内存泄漏
=================
评论