久久精品国产一区二区电影,久久精品国产亚洲av瑜伽,精品无人区一码卡二卡三,久草热8精品视频在线观看 ,久久99精品久久久久麻豆

錘子簡歷品牌推廣師
面試必問:用 Java 寫一個內存泄漏程序
作者:錘子簡歷 2020/02/10 23:15:00
閱讀 125

問題:


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


這題完全沒有思路,


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


回答:


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


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

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

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

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

  5. 重復執(zhí)行。


這個方法之所以奏效,是因為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>可以看到內存不斷增加!在我的系統(tǒng)上,使用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();
   }
 }

 /**
  * 線程的實現只是循環(huán)調用
  * {@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類.這里需要解決一些麻煩
  * 必須確保每次得到一個新的類
  * (而非系統(tǒng)class loader提供的
  * 重用類).如果此子類所在JAR文件不在系統(tǒng)的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方法會創(chuàng)建一個新的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靜態(tài)字段創(chuàng)建了!
   static final byte[] moreBytesToLeak = new byte[1024 * 1024 * 10];
 
   private static final ThreadLocal<LoadedInChildClassLoader> threadLocal
       = new ThreadLocal<>();
   
   public LoadedInChildClassLoader() {
     // 在ThreadLocal中存儲對這個類的引用
     threadLocal.set(this);
   }
 }
}


內容來源說明:本文章來自網絡收集,如侵犯了你的權益,請聯系QQ:2772182309進行刪除。