How can I create a memory leak in Java?

后端 未结 30 1704
没有蜡笔的小新
没有蜡笔的小新 2020-11-21 22:26

I just had an interview, and I was asked to create a memory leak with Java.

Needless to say, I felt pretty dumb having no clue on how to eve

相关标签:
30条回答
  • 2020-11-21 22:29

    A common example of this in GUI code is when creating a widget/component and adding a listener to some static/application scoped object and then not removing the listener when the widget is destroyed. Not only do you get a memory leak, but also a performance hit as when whatever you are listening to fires events, all your old listeners are called too.

    0 讨论(0)
  • Create a static Map and keep adding hard references to it. Those will never be garbage collected.

    public class Leaker {
        private static final Map<String, Object> CACHE = new HashMap<String, Object>();
    
        // Keep adding until failure.
        public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
    }
    
    0 讨论(0)
  • 2020-11-21 22:31

    The following is a pretty pointless example, if you do not understand JDBC. Or at least how JDBC expects a developer to close Connection, Statement and ResultSet instances before discarding them or losing references to them, instead of relying on the implementation of finalize.

    void doWork()
    {
       try
       {
           Connection conn = ConnectionFactory.getConnection();
           PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query
           ResultSet rs = stmt.executeQuery();
           while(rs.hasNext())
           {
              ... process the result set
           }
       }
       catch(SQLException sqlEx)
       {
           log(sqlEx);
       }
    }
    

    The problem with the above is that the Connection object is not closed, and hence the physical connection will remain open, until the garbage collector comes around and sees that it is unreachable. GC will invoke the finalize method, but there are JDBC drivers that do not implement the finalize, at least not in the same way that Connection.close is implemented. The resulting behavior is that while memory will be reclaimed due to unreachable objects being collected, resources (including memory) associated with the Connection object might simply not be reclaimed.

    In such an event where the Connection's finalize method does not clean up everything, one might actually find that the physical connection to the database server will last several garbage collection cycles, until the database server eventually figures out that the connection is not alive (if it does), and should be closed.

    Even if the JDBC driver were to implement finalize, it is possible for exceptions to be thrown during finalization. The resulting behavior is that any memory associated with the now "dormant" object will not be reclaimed, as finalize is guaranteed to be invoked only once.

    The above scenario of encountering exceptions during object finalization is related to another other scenario that could possibly lead to a memory leak - object resurrection. Object resurrection is often done intentionally by creating a strong reference to the object from being finalized, from another object. When object resurrection is misused it will lead to a memory leak in combination with other sources of memory leaks.

    There are plenty more examples that you can conjure up - like

    • Managing a List instance where you are only adding to the list and not deleting from it (although you should be getting rid of elements you no longer need), or
    • Opening Sockets or Files, but not closing them when they are no longer needed (similar to the above example involving the Connection class).
    • Not unloading Singletons when bringing down a Java EE application. Apparently, the Classloader that loaded the singleton class will retain a reference to the class, and hence the singleton instance will never be collected. When a new instance of the application is deployed, a new class loader is usually created, and the former class loader will continue to exist due to the singleton.
    0 讨论(0)
  • 2020-11-21 22:32

    A simple thing to do is to use a HashSet with an incorrect (or non-existent) hashCode() or equals(), and then keep adding "duplicates". Instead of ignoring duplicates as it should, the set will only ever grow and you won't be able to remove them.

    If you want these bad keys/elements to hang around you can use a static field like

    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.
    
    0 讨论(0)
  • 2020-11-21 22:32

    Any time you keep references around to objects that you no longer need you have a memory leak. See Handling memory leaks in Java programs for examples of how memory leaks manifest themselves in Java and what you can do about it.

    0 讨论(0)
  • 2020-11-21 22:32

    The interviewer was probably looking for a circular reference like the code below (which incidentally only leak memory in very old JVMs that used reference counting, which isn't the case any more). But it's a pretty vague question, so it's a prime opportunity to show off your understanding of JVM memory management.

    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 */
    }
    

    Then you can explain that with reference counting, the above code would leak memory. But most modern JVMs don't use reference counting any longer, most use a sweep garbage collector, which will in fact collect this memory.

    Next you might explain creating an Object that has an underlying native resource, like this:

    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. */
        }
    }
    

    Then you can explain this is technically a memory leak, but really the leak is caused by native code in the JVM allocating underlying native resources, which weren't freed by your Java code.

    At the end of the day, with a modern JVM, you need to write some Java code that allocates a native resource outside the normal scope of the JVM's awareness.

    0 讨论(0)
提交回复
热议问题