简体   繁体   English

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

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

I have read somewhere that using the class instances as below is not a good idea as they might cause memory leaks. 我在某处读过如下使用类实例并不是一个好主意,因为它们可能会导致内存泄漏。 Can someone tell me if if that is a valid statement? 有人能告诉我这是否是一个有效的陈述? Or are they any problems using it this way? 或者这样使用它有什么问题吗?

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

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

Yes, you do need to be cautious! 是的,你需要谨慎! For example, if your code is running in a web container and you are in the habit of doing hot deployment of webapps, a retained reference to a single class object can cause a significant permgen memory leak. 例如,如果您的代码在Web容器中运行,并且您习惯于对Web应用程序进行热部署,则对单个类对象的保留引用可能会导致严重的permgen内存泄漏。

This article explains the problem in detail. 本文详细解释了该问题。 But in a nutshell, the problem is that each class contains a reference to its classloader, and each classloader contains references to every class that it has loaded. 但简而言之,问题是每个类都包含对其类加载器的引用,并且每个类加载器都包含对其已加载的每个类的引用。 So if one class is reachable, all of them are. 因此,如果可以访问一个类,则所有类都可以访问。


From Java 8 - Permgen was removed. 从Java 8 - Permgen被删除。 Do you think it is ok to use Class instance as HashMap key in any situations? 您认为在任何情况下都可以将Class实例用作HashMap键吗?

Be aware that you will still have a memory leak. 请注意,您仍会有内存泄漏。 Any dynamicly loaded class used in your HashMap (key or value) and (at least) other dynamically loaded classes will be kept reachable. 在HashMap(键或值)和(至少)其他动态加载的类中使用的任何动态加载的类都将保持可访问状态。 This means the GC won't be able to unload / delete them. 这意味着GC将无法卸载/删除它们。 What was previously a permgen leak is now an ordinary heap leak. 之前的permgen泄漏现在是普通的堆泄漏。

No, that's not a problem. 不,那不是问题。 As long as you were creating an instance of the class anyway, you're not using any more memory by holding a reference to the class itself. 只要你创建了一个类的实例,你就不会通过持有对类本身的引用来使用更多的内存。

As Stephen C mentioned, the memory leak is indeed because of classloaders. 正如Stephen C所提到的,内存泄漏确实是因为类加载器。 But the problem is more acute than at first glance. 但问题比乍一看更为严重。 Consider this: 考虑一下:

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

Furthermore, 此外,

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

A few such static caches can start adding up to serious amounts of memory lost whenever a webapp or some other dynamically (classloaded) loaded app is cycled. 每当webapp或其他动态(类加载)加载的应用程序循环时,一些此类静态缓存可能会开始累积大量内存丢失。

There are several approaches to working around this problem. 有几种方法可以解决这个问题。 If you don't care about different 'versions' of the same class from different classloaders, then simply key based on Class.getName() , which is a java.lang.String . 如果您不关心来自不同类加载器的同一类的不同“版本”,那么只需基于Class.getName()键,这是一个java.lang.String

Another option is to use java.util.WeakHashMap . 另一种选择是使用java.util.WeakHashMap This form of Map only maintains weak references to the keys. 这种形式的Map只保留对键的弱引用。 Weak references don't hold up GC, so they key won't cause a memory accumulation. 弱引用不会阻止GC,因此它们的键不会导致内存累积。 However, the values are not referenced weakly. 但是,这些值不会被弱引用。 So if the values are for example instances of the classes used as keys, the WeakHashMap does not work. 因此,如果值是例如用作键的类的实例,则WeakHashMap不起作用。

That depends where the reference classToInstance=new HashMap(); 这取决于引用classToInstance=new HashMap(); is defined. 被定义为。

A class points back to its class loader, and as a consequence, the class loader can not be garbage collected while a reference to the class exists. 类指向其类加载器,因此,当存在对类的引用时,不能对类加载器进行垃圾回收。 But if the references form a circle (or an unreachable cluster), this still works -- the GC knows how to deal with circular references. 但是如果引用形成一个圆(或一个不可达的簇),这仍然有效 - GC知道如何处理循环引用。

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

So a reference to a class may prevent the class loader from being GC'ed only if the references come from an object/class in a parent class loader. 因此,只有当引用来自类加载器中的对象/类时,对类的引用才可能阻止类加载器被GC。

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

That's what the sentence " any reference from outside the application to an object in the application of which the class is loaded by the application's classloader will cause a classloader leak " means in the article mentioned in Stephen C. answer. 这就是句子“ 应用程序外部的任何引用,应用程序的类加载器加载类的应用程序中的任何引用将导致类加载器泄漏 ”,这意味着在Stephen C.回答中提到的文章中。

But it's shouldn't be the case if the map is part of your application. 但如果地图是您的应用程序的一部分,情况应该不是这样。

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

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