繁体   English   中英

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

[英]Is using the Class instance as a Map key a best practice?

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

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

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

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

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


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

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

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

正如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不起作用。

这取决于引用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
                    \-------------------/

这就是句子“ 应用程序外部的任何引用,应用程序的类加载器加载类的应用程序中的任何引用将导致类加载器泄漏 ”,这意味着在Stephen C.回答中提到的文章中。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM