我刚刚接受了一次采访,被要求用Java创建内存泄漏。
不用说,我觉得自己很傻,不知道如何开始创作。
什么样的例子?
我刚刚接受了一次采访,被要求用Java创建内存泄漏。
不用说,我觉得自己很傻,不知道如何开始创作。
什么样的例子?
当前回答
这很简单:
Object[] o = new Object[]{};
while(true) {
o = new Object[]{o};
}
其他回答
这里有一个在纯Java中创建真正的内存泄漏(运行代码无法访问但仍存储在内存中的对象)的好方法:
应用程序创建一个长时间运行的线程(或者使用线程池更快地泄漏)。线程通过(可选的自定义)ClassLoader加载类。该类分配一大块内存(例如新字节[10000000]),在静态字段中存储对它的强引用,然后在ThreadLocal中存储对自身的引用。分配额外的内存是可选的(泄漏类实例就足够了),但这会使泄漏工作得更快。应用程序清除对自定义类或从中加载该类的ClassLoader的所有引用。重复
由于ThreadLocal在Oracle的JDK中的实现方式,这会造成内存泄漏:
每个线程都有一个私有字段threadLocals,它实际上存储线程本地值。此映射中的每个键都是对ThreadLocal对象的弱引用,因此在ThreadLocal对象被垃圾收集后,其条目将从映射中删除。但每个值都是一个强引用,因此当一个值(直接或间接)指向作为其键的ThreadLocal对象时,只要线程存在,该对象既不会被垃圾收集,也不会从映射中删除。
在本例中,强引用链如下所示:
线程对象→ threadLocals映射→ 示例类的实例→ 示例类→ 静态ThreadLocal字段→ ThreadLocal对象。
(ClassLoader在创建泄漏时并没有真正发挥作用,它只是因为这个额外的引用链而使泄漏变得更糟:example类→ 类加载器→ 它加载的所有类。在许多JVM实现中,尤其是在Java7之前,情况更糟,因为类和ClassLoader被直接分配到permagen中,根本不会被垃圾收集。)
这种模式的一个变体是,如果您经常重新部署碰巧使用ThreadLocal的应用程序,而这些应用程序在某种程度上指向自己,那么应用程序容器(如Tomcat)会像筛子一样泄漏内存。这种情况可能有许多微妙的原因,并且通常很难调试和/或修复。
更新:由于很多人一直在要求它,这里有一些示例代码显示了这种行为。
也许通过JNI使用外部本机代码?
使用纯Java,这几乎是不可能的。
但这是一种“标准”类型的内存泄漏,即您无法再访问内存,但它仍然属于应用程序。相反,您可以保留对未使用对象的引用,或者打开流而不关闭它们。
在具有自己生命周期的类中随意使用非静态内部类。
在Java中,非静态内部类和匿名类对其外部类具有隐式引用。另一方面,静态内部类则不然。
下面是一个常见的Android内存泄漏示例,但这并不明显:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() { // Non-static inner class, holds the reference to the SampleActivity outer class
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for a long time.
mLeakyHandler.postDelayed(new Runnable() {//here, the anonymous inner class holds the reference to the SampleActivity class too
@Override
public void run() {
//....
}
}, SOME_TOME_TIME);
// Go back to the previous Activity.
finish();
}}
这将防止活动上下文被垃圾收集。
重叠侦听器是内存泄漏的一个很好的例子:对象被添加为侦听器。当不再需要对象时,对象的所有引用都为空。然而,忘记从侦听器列表中删除对象会使对象保持活动状态,甚至对事件做出响应,从而浪费内存和CPU。看见http://www.drdobbs.com/jvm/java-qa/184404011
内存泄漏的情况有很多种。我遇到了一个,它暴露了一个不应该在其他地方暴露和使用的地图。
public class ServiceFactory {
private Map<String, Service> services;
private static ServiceFactory singleton;
private ServiceFactory() {
services = new HashMap<String, Service>();
}
public static synchronized ServiceFactory getDefault() {
if (singleton == null) {
singleton = new ServiceFactory();
}
return singleton;
}
public void addService(String name, Service serv) {
services.put(name, serv);
}
public void removeService(String name) {
services.remove(name);
}
public Service getService(String name, Service serv) {
return services.get(name);
}
// The problematic API, which exposes the map.
// and user can do quite a lot of thing from this API.
// for example, create service reference and forget to dispose or set it null
// in all this is a dangerous API, and should not expose
public Map<String, Service> getAllServices() {
return services;
}
}
// Resource class is a heavy class
class Service {
}