使用Class实例作为Map键是最佳实践吗?

时间:2010-04-12 21:21:45

标签: java

我在某处读过如下使用类实例并不是一个好主意,因为它们可能会导致内存泄漏。有人能告诉我这是否是一个有效的陈述?或者他们这样使用它有什么问题吗?

Map<Class<?>,String> classToInstance = new HashMap();

classToInstance.put(String.class,"Test obj");

4 个答案:

答案 0 :(得分:20)

是的,你需要谨慎!例如,如果您的代码在Web容器中运行,并且您习惯于对Web应用程序进行热部署,则对单个类对象的保留引用可能会导致严重的permgen内存泄漏。

This article详细解释了这个问题。但简而言之,问题是每个类都包含对其类加载器的引用,并且每个类加载器都包含对它已加载的每个类的引用。因此,如果一个类可以访问,则所有类都是。


  

从Java 8 - Permgen被删除。你认为在任何情况下都可以将Class实例用作HashMap键吗?

请注意,您仍会有内存泄漏。在HashMap(键或值)和(至少)其他动态加载的类中使用的任何动态加载的类都将保持可访问状态。这意味着GC将无法卸载/删除它们。之前的permgen泄漏现在是普通的堆泄漏。

答案 1 :(得分:5)

不,这不是问题。只要你创建了一个类的实例,你就不会通过持有对类本身的引用来使用更多的内存。

答案 2 :(得分:2)

正如Stephen C所提到的,内存泄漏确实是因为类加载器。但问题比乍一看更为严重。考虑一下:

mapkey --> class --> classloader --> all other classes defined by this classloader.

此外,

class --> any static members, including static Maps e.g. caches.

当webapp或其他动态(类加载)加载的应用程序循环时,一些此类静态缓存可能会开始累积大量内存丢失。

有几种方法可以解决这个问题。如果您不关心来自不同类加载器的同一类的不同“版本”,则只需根据Class.getName()进行键控,即java.lang.String

另一种选择是使用java.util.WeakHashMap。这种形式的Map只保留对键的弱引用。弱引用不会阻止GC,因此它们的键不会导致内存累积。但是,值弱引用。因此,如果值是例如用作键的类的实例,则WeakHashMap不起作用。

答案 3 :(得分:0)

这取决于定义参考classToInstance=new HashMap();的位置。

一个类指向它的类加载器,因此,当存在对类的引用时,不能对类加载器进行垃圾回收。但是如果引用形成一个圆(或一个不可达的簇),这仍然有效 - GC知道如何处理循环引用。

parent class loader --> class loader <--> class  // no GC is possible
parent class loader     class loader <--> class  // circular references are GC'ed

因此,只有当引用来自类加载器中的对象/类时,对类的引用才可能阻止类加载器成为GC。

parent class loader     class loader <--> class  // no GC is possible
                    \-------------------/

这就是句子“从应用程序外部向应用程序的类加载器加载类的应用程序中的对象的任何引用将导致类加载器泄漏”意味着{{3}在Stephen C.回答中提到过。

但如果地图是您的应用程序的一部分,则情况不应该如此。