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

新手上路,请多包涵

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

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

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

原文由 Aravind Yarram 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

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

这篇文章 详细解释了这个问题。但简而言之,问题在于每个类都包含对其类加载器的引用,而每个类加载器都包含对其加载的每个类的引用。因此,如果一个班级是可达的,那么所有班级都是。

另一件需要注意的事情是,如果您用作键的其中一个类被重新加载,那么:

  1. 该类的新旧版本将不相等。
  2. 查找新类最初会给出一个“miss”。
  3. 将新类添加到地图后,您现在将有两个不同版本的类的不同地图条目。
  4. 即使类的两个版本之间没有 代码 差异,这也适用。它们之所以不同,仅仅是因为它们是由不同的类加载器加载的。

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

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

以前的 permgen 泄漏现在是普通的堆和元空间存储泄漏。 (元空间是保存类的类描述符和代码对象的地方。)

原文由 Stephen C 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

此外,

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

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

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

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

原文由 Dilum Ranatunga 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题