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

Ara*_*ram 37 java

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

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

classToInstance.put(String.class,"Test obj");
Run Code Online (Sandbox Code Playgroud)

Ste*_*n C 23

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

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


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

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

  • 从 Java 8 - Permgen 被删除。您认为在任何情况下都可以使用 Class 实例作为 HashMap 键吗?谢谢! (2认同)

Mic*_*ers 5

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

  • @Chris Knight:不,可达性只会前进.具有对`String.class`的引用的`classToInstanceMap`将阻止收集`String.class`,但不会反过来.`String.class`可能引用了一个不符合收集条件的类型信息块,但我对JVM的了解还不够. (2认同)

Dil*_*nga 5

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

mapkey --> class --> classloader --> all other classes defined by this classloader.
Run Code Online (Sandbox Code Playgroud)

此外,

class --> any static members, including static Maps e.g. caches.
Run Code Online (Sandbox Code Playgroud)

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

有几种方法可以解决这个问题。如果您不关心来自不同类加载器的同一类的不同“版本”,那么只需基于 键即可Class.getName(),它是一个java.lang.String.

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