日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

面试必问:用 Java 写一个内存泄漏程序

發布時間:2025/3/16 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面试必问:用 Java 写一个内存泄漏程序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

編譯:ImportNew/唐尤華

原文鏈接:stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java

問題:

剛參加的一個面試,要我用Java寫一個內存泄露程序。

這題完全沒有思路,

有好心人能給出一個例子嗎?

回答:

造成內存泄漏,就是讓運行的程序無法訪問存儲在內存中的對象,下面是Java實現:

  • 創建一個長時間運行的線程(使用線程池泄露的速度更快)。

  • 線程通過ClassLoader加載某個類(也可以用自定義ClassLoader)。

  • 這個類分配了大量內存(例如new byte[1000000]),賦給靜態字段存儲對它的強引用,然后在ThreadLocal中存儲對自身的引用。還可以分配額外的內存,這樣泄漏的速度更快(其實只要泄漏Class實例就足夠了)。

  • 這個線程會清除所有自定義類及加載它的ClassLoader的引用。

  • 重復執行。

  • 這個方法之所以奏效,是因為ThreadLocal保留了對該對象的引用,對象引用保留了對Class的引用,而Class引用又保留了對ClassLoader的引用。反過來,ClassLoader會保留通過它加載的所有類的引用。

    (在許多JVM實現中情況更糟,尤其Java 7之前版本。因為Class和ClassLoader會直接分配到permgen中,GC不進行回收)。但是,無論JVM如何處理類卸載,ThreadLocal仍然會阻止被回收的Class對象)。

    這種方案還可以變化為,頻繁地重新部署碰巧用到ThreadLocal的應用程序。這時像Tomcat這樣的應用程序容器會像篩子一樣泄漏內存。(因為應用程序容器會像上面那樣啟動線程,并且每次重新部署應用程序時,都會使用新的ClassLoader)

    更新:鑒于大家強烈要求,這里給出一個演示程序。

    ClassLoaderLeakExample.java

    import java.io.IOException;
    import java.net.URLClassLoader;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.nio.file.Path;

    /**
    * ClassLoader泄漏演示
    *
    * <p>要查看實際運行效果,請將此文件復制到某個臨時目錄,
    * 然后運行:
    * <pre>{@code
    * javac ClassLoaderLeakExample.java
    * java -cp .ClassLoaderLeakExample
    * }</pre>
    *
    * <p>可以看到內存不斷增加!在我的系統上,使用JDK 1.8.0_25,開始
    * 短短幾秒鐘就收到了OutofMemoryErrors
    *
    * <p>這個類用到了一些Java 8功能,主要用于
    * I/O 操作同樣的原理可以適用于
    * Java 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();
    }
    }

    /**
    * 線程的實現只是循環調用
    * {@link #loadAndDiscard()}
    */
    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;
    }
    }
    }
    }

    /**
    * 這是一個簡單的ClassLoader實現,只能加載一個類
    * 即LoadedInChildClassLoader類.這里需要解決一些麻煩
    * 必須確保每次得到一個新的類
    * (而非系統class loader提供的
    * 重用類).如果此子類所在JAR文件不在系統的classpath中,
    * 不需要這么麻煩.
    */
    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方法會創建一個新的ClassLoader, 加載一個類,
    * 然后丟棄對它們的所有引用.從理論上講,應該不會影響GC
    * 因為沒有引用可以逃脫該方法! 但實際上,
    * 結果會像篩子一樣泄漏內存.
    */
    static void loadAndDiscard() throws Exception {
    ClassLoader childClassLoader = new ChildOnlyClassLoader();
    Class<?> childClass = Class.forName(
    LoadedInChildClassLoader.class.getName(), true, childClassLoader);
    childClass.newInstance();
    // 該方法返回時,將無法訪問
    // childClassLoader或childClass的引用,
    // 但是這些對象仍會成為GC Root!
    }

    /**
    * 一個看起來人畜無害的類,沒有做什么特別的事情.
    */
    public static final class LoadedInChildClassLoader {
    // 獲取一些bytes.對于泄漏不是必需的,
    // 只是讓效果出得更快一些.
    // 注意:這里開始真正泄露內存,這些bytes
    // 每次迭代都為這個final靜態字段創建了!
    static final byte[] moreBytesToLeak = new byte[1024 * 1024 * 10];

    private static final ThreadLocal<LoadedInChildClassLoader> threadLocal
    = new ThreadLocal<>();

    public LoadedInChildClassLoader() {
    // 在ThreadLocal中存儲對這個類的引用
    threadLocal.set(this);
    }
    }
    }

    有道無術,術可成;有術無道,止于術

    歡迎大家關注Java之道公眾號

    好文章,我在看??

    總結

    以上是生活随笔為你收集整理的面试必问:用 Java 写一个内存泄漏程序的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。