class BadKey {// no hashCode or equals();public final String key;public BadKey(String key) { this.key = key; }}
Map map = System.getProperties();map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.
public E remove(int index) {RangeCheck(index);
modCount++;E oldValue = (E) elementData[index];
int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index + 1, elementData, index,numMoved);elementData[--size] = null; // (!) Let gc do its work
return oldValue;}
创建线程继承了ContextClassLoader和AccessControlContext,加上ThreadGroup和任何InheritedThreadLocal,所有这些引用都是潜在的泄漏,以及类加载器加载的整个类和所有静态引用,以及ja-ja。这种效果在整个j. u. c. Execator框架中尤为明显,该框架具有超级简单的ThreadFactory接口,但大多数开发人员对潜伏的危险一无所知。此外,许多库确实应要求启动线程(行业流行库太多了)。
public class Example1 {public Example2 getNewExample2() {return this.new Example2();}public class Example2 {public Example2() {}}}
现在,如果您调用它并得到一个丢弃它的实例2,您将本质上仍然有一个指向实例1对象的链接。
public class Referencer {public static Example2 GetAnExample2() {Example1 ex = new Example1();return ex.getNewExample2();}
public static void main(String[] args) {Example2 ex = Referencer.GetAnExample2();// As long as ex is reachable; Example1 will always remain in memory.}}
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 exposepublic Map<String, Service> getAllServices() {return services;}
}
// Resource class is a heavy classclass Service {
}
一种可能是为ArrayList创建一个包装器,它只提供一种方法:向ArrayList添加东西的方法。使ArrayList本身私有。现在,在全局范围内构造其中一个包装器对象(作为类中的静态对象)并使用最终关键字(例如public static final ArrayListWrapper wrapperClass = new ArrayListWrapper())对其进行限定。所以现在引用不能被改变。也就是说,wrapperClass = null不会起作用,也不能用于释放内存。但是除了向它添加对象之外,也没有办法对wrapperClass做任何事情。因此,你添加到wrapperClass的任何对象都不可能回收。
public class StringLeaker{private final String muchSmallerString;
public StringLeaker(){// Imagine the whole Declaration of Independence hereString veryLongString = "We hold these truths to be self-evident...";
// The substring here maintains a reference to the internal char[]// representation of the original string.this.muchSmallerString = veryLongString.substring(0, 1);}}
import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.util.zip.ZipEntry;import java.util.zip.ZipOutputStream;
public class BigJarCreator {public static void main(String[] args) throws IOException {ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar")));zos.putNextEntry(new ZipEntry("resource.txt"));zos.write("not too much in here".getBytes());zos.closeEntry();zos.putNextEntry(new ZipEntry("largeFile.out"));for (int i=0 ; i<10000000 ; i++) {zos.write((int) (Math.round(Math.random()*100)+20));}zos.closeEntry();zos.close();}}
只需粘贴到名为BigJarCreator.java的文件中,编译并从命令行运行它:
javac BigJarCreator.javajava -cp . BigJarCreator
Et voilà:您在当前工作目录中找到一个jar存档,其中包含两个文件。
让我们创建第二个类:
public class MemLeak {public static void main(String[] args) throws InterruptedException {int ITERATIONS=100000;for (int i=0 ; i<ITERATIONS ; i++) {MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");}System.out.println("finished creation of streams, now waiting to be killed");
Thread.sleep(Long.MAX_VALUE);}
}
public class OutOfMemory {
public static void main(String[] arg) {
List<Long> mem = new LinkedList<Long>();while (true) {mem.add(new Long(Long.MAX_VALUE));}}}
private static final Map<String, Info> myCache = new HashMap<>();
public void getInfo(String key){// uses cacheInfo info = myCache.get(key);if (info != null) return info;
// if it's not in cache, then fetch it from the databaseinfo = Database.fetch(key);if (info == null) return null;
// and store it in the cachemyCache.put(key, info);return info;}
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() { // Non-static inner class, holds the reference to the SampleActivity outer class@Overridepublic void handleMessage(Message msg) {// ...}}
@Overrideprotected 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@Overridepublic void run() {//....}}, SOME_TOME_TIME);
// Go back to the previous Activity.finish();}}
class A {B bRef;}
class B {A aRef;}
public class Main {public static void main(String args[]) {A myA = new A();B myB = new B();myA.bRef = myB;myB.aRef = myA;myA=null;myB=null;/* at this point, there is no access to the myA and myB objects, *//* even though both objects still have active references. */} /* main */}
public class Main {public static void main(String args[]) {Socket s = new Socket(InetAddress.getByName("google.com"),80);s=null;/* at this point, because you didn't close the socket properly, *//* you have a leak of a native descriptor, which uses memory. */}}
public class LeakTest {
private final List<EntryHolder> holdersCache = new ArrayList<>();private static final int MAP_SIZE = 100_000;
public void run() {// create 500 entries each holding a reference to an Entry of a TreeMapIntStream.range(0, 500).forEach(value -> {// create mapfinal Map<String, Integer> map = pseudoQueryDatabase();
final int index = new Random().nextInt(MAP_SIZE);
// get random entry from mapfor (Map.Entry<String, Integer> entry : map.entrySet()) {if (entry.getValue().equals(index)) {holdersCache.add(new EntryHolder(entry));break;}}// to observe behavior in visualvmtry {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}});
}
public static Map<String, Integer> pseudoQueryDatabase() {final Map<String, Integer> map = new TreeMap<>();IntStream.range(0, MAP_SIZE).forEach(i -> map.put(String.valueOf(i), i));return map;}
public static void main(String[] args) throws Exception {new LeakTest().run();}}
// Can you spot the "memory leak"?public class Stack {private static final int DEFAULT_INITIAL_CAPACITY = 16;private Object[] elements;private int size = 0;
public Stack() {elements = new Object[DEFAULT_INITIAL_CAPACITY];}
public void push(Object e) {ensureCapacity();elements[size++] = e;}
public Object pop() {if (size == 0) throw new EmptyStackException();return elements[--size];}
/*** Ensure space for at least one more element, roughly* doubling the capacity each time the array needs to grow.*/private void ensureCapacity() {if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1);}}