本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看活动链接
提问:如何在java创建内存泄漏?
我刚接受了一次面试,我被要求用Java创建一个内存泄漏。
不用说,我觉得很愚蠢,甚至不知道如何开始创建一个。
一个例子是什么?
高分回答:
下面是在纯 Java 中创建真实内存泄漏(通过运行代码无法访问的对象但仍存储在内存中)的好方法:
1.该应用程序创建一个长运行的线程(或使用螺纹池泄漏甚至更快)。
2.线程通过(可选自定义)加载类。ClassLoader
3.类分配了大量内存(例如),在静态字段中存储对它的强引用,然后将引用存储在 a 中。分配额外的内存是可选的(泄漏类实例就足够了),但它会使泄漏工作得更快。new byte[1000000]ThreadLocal
该应用程序清除了自定义类或其加载的所有引用。ClassLoader
重复。
由于甲骨文的 JDK 中实现了此方法,这会产生内存泄漏:ThreadLocal
每个字段都有一个私有字段,实际上存储线程本地值。ThreadthreadLocals 此地图中的每个键都是对对象的弱引用,因此该对象被垃圾收集后,其条目将从地图中删除。ThreadLocalThreadLocal 但每个值都是一个强有力的参考,因此,当一个值(直接或间接)指向其关键对象时,只要线程保持生命,该对象既不会被垃圾收集,也不会从地图上删除。ThreadLocal 在此示例中,强引用链看起来像这样:
Thread对象→映射示例类→示例类→静态场→对象的示例类→实例。threadLocalsThreadLocalThreadLocal
(在创建泄漏方面,该漏洞并没有真正发挥作用,它只是由于此附加的参考链而使泄漏变得更糟:示例类→ →它加载的所有类。在许多 JVM 实现中,情况更糟,尤其是在 Java 7 之前,因为类和 s 被直接分配到永久基因中,并且根本不收集垃圾。ClassLoaderClassLoaderClassLoader
这种模式的一个变化是,为什么应用程序容器(如Tomcat)可以泄漏内存像筛子,如果你经常重新部署的应用程序,碰巧使用s,以某种方式指向自己。这可能发生在许多微妙的原因,往往很难调试和/或修复。ThreadLocal
import java.io.IOException; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.Path; /** * Example demonstrating a ClassLoader leak. 演示类加载器泄漏的示例。 * * <p>To see it in action, copy this file to a temp directory somewhere, * and then run:o查看它的运行情况,将此文件复制到某个临时目录中,然后跑 * <pre>{@code * javac ClassLoaderLeakExample.java * java -cp . ClassLoaderLeakExample * }</pre> * * <p>And watch the memory grow! On my system, using JDK 1.8.0_25, I start * getting OutofMemoryErrors within just a few seconds.看着记忆成长!在我的系统上,使用jdk1.8.0\u25 *只需几秒钟就可以摆脱内存错误 * * <p>This class is implemented using some Java 8 features, mainly for * convenience in doing I/O. The same basic mechanism works in any version * of Java since 1.2. 这个类是使用一些Java8特性实现的,主要用于 *方便进行I/O。相同的基本机制适用于任何版本 *从1.2开始的Java。 */ public final class ClassLoaderLeakExample { static volatile boolean running = true; public static void main(String[] args) throws Exception { Thread thread = new LongRunningThread(); try { thread.start(); System.out.println("Running, press any key to stop."); System.in.read(); } finally { running = false; thread.join(); } } /** * Implementation of the thread. It just calls {@link #loadAndDiscard()} * in a loop. */ static final class LongRunningThread extends Thread { @Override public void run() { while(running) { try { loadAndDiscard(); } catch (Throwable ex) { ex.printStackTrace(); } try { Thread.sleep(100); } catch (InterruptedException ex) { System.out.println("Caught InterruptedException, shutting down."); running = false; } } } } /** * A simple ClassLoader implementation that is only able to load one * class, the LoadedInChildClassLoader class. We have to jump through * some hoops here because we explicitly want to ensure we get a new * class each time (instead of reusing the class loaded by the system * class loader). If this child class were in a JAR file that wasn't * part of the system classpath, we wouldn't need this mechanism. */ static final class ChildOnlyClassLoader extends ClassLoader { ChildOnlyClassLoader() { super(ClassLoaderLeakExample.class.getClassLoader()); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { if (!LoadedInChildClassLoader.class.getName().equals(name)) { return super.loadClass(name, resolve); } try { Path path = Paths.get(LoadedInChildClassLoader.class.getName() + ".class"); byte[] classBytes = Files.readAllBytes(path); Class<?> c = defineClass(name, classBytes, 0, classBytes.length); if (resolve) { resolveClass(c); } return c; } catch (IOException ex) { throw new ClassNotFoundException("Could not load " + name, ex); } } } /** * Helper method that constructs a new ClassLoader, loads a single class, * and then discards any reference to them. Theoretically, there should * be no GC impact, since no references can escape this method! But in * practice this will leak memory like a sieve. 构造一个新的类加载器,加载一个类, *然后丢弃对它们的任何引用。理论上,应该有 *没有GC影响,因为没有引用可以转义这个方法!但是在 *练习这个会像筛子一样漏掉记忆。 */ static void loadAndDiscard() throws Exception { ClassLoader childClassLoader = new ChildOnlyClassLoader(); Class<?> childClass = Class.forName( LoadedInChildClassLoader.class.getName(), true, childClassLoader); childClass.newInstance(); // When this method returns, there will be no way to reference // childClassLoader or childClass at all, but they will still be // rooted for GC purposes! } /** * An innocuous-looking class. Doesn't do anything interesting. */ public static final class LoadedInChildClassLoader { // Grab a bunch of bytes. This isn't necessary for the leak, it just // makes the effect visible more quickly. // Note that we're really leaking these bytes, since we're effectively // creating a new instance of this static final field on each iteration! static final byte[] moreBytesToLeak = new byte[1024 * 1024 * 10]; private static final ThreadLocal<LoadedInChildClassLoader> threadLocal = new ThreadLocal<>(); public LoadedInChildClassLoader() { // Stash a reference to this class in the ThreadLocal threadLocal.set(this); } } } 复制代码
简单的说: 不断创建对象。
高分回答:
静态字段保持对象引用[特别是最终字段]
class MemorableClass { static final ArrayList list = new ArrayList(100); } 复制代码
在长字符串上调用字符串 String.intern()
String str=readString(); // read lengthy string any source db,textbox/jsp etc.. // This will place the string in memory pool from which you can't remove str.intern(); 复制代码
(未关闭)开放流(文件、网络等)
try { BufferedReader br = new BufferedReader(new FileReader(inputFile)); ... ... } catch (Exception e) { e.printStacktrace(); } 复制代码
未关闭的连接
try { Connection conn = ConnectionFactory.getConnection(); ... ... } catch (Exception e) { e.printStacktrace(); } 复制代码
JVM 垃圾收集器无法到达的区域,例如通过本地方法分配的内存
在 Web 应用程序中,某些对象存储在应用范围中,直到应用程序被明确停止或删除。
getServletContext().setAttribute("SOME_MAP", map); 复制代码
不正确或不适当的 JVM 选项,例如 IBM JDK 上的选项,可防止未使用的类垃圾收集noclassgc
参见IBM jdk 设置。
文章翻译自 stackoverflow.com/questions/6…
作者建议: 上面说的每一种都要时刻注意
真心感谢帅逼靓女们能看到这里,如果这个文章写得还不错,觉得有点东西的话
求点赞???? 求关注❤️ 求分享???? 对8块腹肌的我来说真的 非常有用!!!
如果本篇博客有任何错误,请批评指教,不胜感激 !❤️❤️❤️❤️