Note source: Silicon Valley JVM full tutorial, one million playback, the whole network peak (Song Hongkang detailed Java virtual machine)

Synchronous update: https://gitee.com/vectorx/NOT…

https://codechina.csdn.net/qq…

https://github.com/uxiahnan/N…

[toc]

Added: Shallow heap and deep heap with memory leaks

1. Shallow Heap

Shallow heap is the amount of memory consumed by an object. In 32-bit systems, an object reference takes up 4 bytes, an int takes up 4 bytes, a long variable takes up 8 bytes, and each object header takes up 8 bytes. Depending on the heap snapshot format, the size of the object may be aligned with 8 bytes.

Take String as an example: 2 int values take up 8 bytes, object reference takes up 4 bytes, object head 8 bytes, total 20 bytes, align to 8 bytes, so account for 24 bytes. () in the jdk7

int hash32 0
int hash 0
ref value C:\Users\Administrat

This 24 bytes is the shallow heap size of the String object. It is independent of the actual value of the String, and the shallow heap size is always 24 bytes, regardless of the String length.

2. Retained Set

The retention set of object A refers to the set of all objects (including object A itself) that can be released after object A is garbage collected, that is, the retention set of object A can be considered as the set of all objects that can only be accessed directly or indirectly through object A. In colloquial terms, it means the collection of objects held only by object A.

Retained Heap

The deep heap is the sum of the shallow heap sizes of all objects in the reserved set of objects.

Note: Shallow heap refers to the memory occupied by the object itself, not including the size of its internal reference object. The deep heap of an object is the sum of the shallow heaps of all objects that can only be accessed (directly or indirectly) through the object. This is the real space that can be freed after the object is collected.

4. The actual size of the object

Here, the actual size of an object is defined as the sum of the shallow heap sizes of all objects that an object can touch, which is what we normally call the object size. This may seem more intuitive and acceptable in everyday development than deep heaps, but in reality, the concept has nothing to do with garbage collection.

The following diagram shows A simple object reference diagram, with object A referencing C and D and object B referencing C and E. Then the shallow heap size of object A is just A itself, without C and D, and the actual size of object A is the sum of A, C and D. The deep heap size of A is the sum of A and D, and since Object C is also accessible through Object B, it is not in the deep heap range of Object A.

5. Dominator Tree

The idea of a dominant tree comes from graph theory. MAT provides an object graph called the Dominator Tree. Domination trees represent the dominant relationships between object instances. In an object reference graph, object A is said to dominate object B if all paths to object B pass through object A. If object A is the dominant object closest to object B, object A is considered to be the direct dominant of object B. Domination tree is based on reference graph between objects. It has the following basic properties:

  • The subtree of Object A (the set of all objects dominated by Object A) represents the retained set of Object A, namely, the deep heap.
  • If object A dominates object B, then the immediate dominator of object A also dominates object B.
  • The edges of the governing tree do not correspond directly to the edges of the object reference graph.

As shown in the figure below, the left graph represents the object reference graph, and the right graph represents the governing tree to which the left graph corresponds. Objects A and B are directly governed by the root object, and since it is possible to pass through A or B on the path to C, the direct dominion of C is also the root object. Object F is referenced to object D because any path to object F must go through object D. Therefore, object D is a direct dominator of object F. Any path to D must go through C. Even a reference from F to D, starting from the root node, also goes through C. So, D is directly dominated by C. In the same way, object E dominates object G. What can reach H can either go through D or E, so neither D nor E can dominate H, while what can go through C can reach both D and E, so C is the direct dominator of H.

6. A memory leak

Accessibility analysis algorithms to determine whether an object is no longer used are essentially to determine whether an object is still referenced. In this case, there are many kinds of memory leaks that can occur due to the implementation of the code (causing the JVM to mistakenly assume that this object is still in reference and cannot be reclaimed, causing memory leaks).

Is > still in use? is

Is > still needed? no

Strictly speaking, a memory leak is only when the objects are no longer used by the program, but the GC cannot reclaim them. But the reality is that a lot of times bad practices (or negligence) can cause an object to have a long lifetime or even a 00M lifetime, which can also be called a “memory leak” in the broad sense.

As shown in the figure below, when Y’s life cycle ends, X is still referring to Y. In this case, object Y will not be collected during garbage collection period. If object X also refers to objects A, B, and C with relatively short life cycles, and object A refers to objects A, B, and C, then A large number of useless objects may not be recycled, thus occupying memory resources and causing memory leaks until memory overflow.

For example, there is a total of 1024M of memory, and the allocated 512M of memory is not recycled, so there is only 512M of memory available, as if part of it was leaked. In plain English, a memory leak is a manger.

7. Out of Memory

When applying for memory, there is not enough memory available. Generally speaking, a toilet on three pits, there are two standing pit do not go (memory leak), the last pit, the toilet said the reception pressure is very big, at this time a sudden to two people, pit (memory) is not enough, memory leak into memory overflow. It can be seen that the relationship between memory leak and memory overflow: the increase of memory leak will eventually lead to memory overflow.

< Mark > Leak Classification </ Mark >

  • Frequently: code that leaks memory is executed multiple times, with each execution leaking a chunk of memory.
  • Accidental: happens under certain circumstances
  • One-time: Methods that leak memory are executed only once;
  • Implicit leakage: It holds memory until the end of execution. This is not strictly a memory leak, as it is eventually released, but it can also cause memory to run out if the execution takes too long.

8. Eight cases of memory leaks in Java

8.1. Static collection classes

Static collection classes such as HashMap, LinkedList, and so on. If these containers are static, their lifetime is the same as that of the JVM program, and the objects in the container cannot be released before the program ends, resulting in a memory leak. Simply put, a long-lived object holds a reference to a short-lived object, and even though the short-lived object is no longer used, it cannot be reclaimed because the long-lived object holds its reference.

public class MemoryLeak { static List list = new ArrayList(); Public void oomTests(){Object obj = new Object(); // List. Add (obj); }}

8.2. Singleton pattern

The singleton pattern, similar to static collections, causes a memory leak. Because of the static nature of a singleton, its lifetime is the same as the lifetime of the JVM, so if the singleton object holds a reference to an external object, then that external object will not be reclaimed, then it will cause a memory leak.

8.3. Inner classes hold outer classes

The inner class holds the outer class if a method of an instance object of the outer class returns an instance object of the inner class. This inner class object is referenced for a long time. Even if the outer class instance object is no longer used, because the inner class holds an instance of the outer class, the outer class object will not be garbage collected, which can also cause a memory leak.

8.4. Various connections, such as database connections, network connections, IO connections, etc

In the process of operating on the database, the connection to the database needs to be established first. When it is no longer in use, the close method needs to be called to release the connection to the database. Only after the connection is closed will the garbage collector collect the corresponding object. Otherwise, an implicit closing of a Connection, Statement, or ResultSet during database access will result in a large number of objects that cannot be reclaimed, resulting in a memory leak.

public static void main(String[] args) { try{ Connection conn =null; Class.forName("com.mysql.jdbc.Driver"); conn =DriverManager.getConnection("url","",""); Statement stmt =conn.createStatement(); ResultSet rs =stmt.executeQuery("...." ); } catch (Exception E) {// Exception log} finally {// 1. Close the result set Statement // 2. Close the declared object resultSet // 3. Close Connection}}

8.5. Unreasonable scope of variables

Inappropriate scope for variables. In general, the definition of a variable has more scope than it can use, which is likely to cause a memory leak. On the other hand, if an object is not set to null in a timely manner, it is very likely that a memory leak will occur.

public class UsingRandom { private String msg; public void receiveMsg(){ readFromNet(); SaveDB (); saveDB(); saveDB(); // save MSG to database}}

For example, the pseudo-code above saves the received message in the variable MSG through the readFromNet method, and then calls the saveDB method to save the contents of MSG to the database. At this time, MSG is already useless. Since MSG’s life cycle is the same as the life cycle of the object, MSG can not be recycled at this time. This caused a memory leak. In fact, the MSG variable can be placed inside the receiveMsg method. When the method is used up, the life cycle of the MSG will be over and it can be recycled. Another option is to set MSG to null after using it, so that the garbage collector will also reclaim the memory space of MSG.

8.6. Change the hash value

Change the hash value. After an object is stored in a HashSet, you cannot change the fields in the object that are involved in the hash calculation.

Otherwise, the modified hash value of the object will not be the same as the hash value originally stored in the HashSet collection. In this case, even if the contains method retrieving an object from the HashSet collection using the current reference of the object as an argument, the object will not be found. This also leads to the inability to remove the current object separately from the HashSet collection, causing a memory leak.

This is why strings are made immutable. We can safely store a String in a HashSet, or treat a String as the key of a HashMap;

When we want to save our own classes into a hash table, we need to make sure that the object’s hashCode is immutable.

Public class ChangeHashCode {public static void main(String[] args) {hashSet set = new HashSet(); Person p1 = new Person(1001, "AA"); Person p2 = new Person(1002, "BB"); set.add(p1); set.add(p2); p1.name = "CC"; Set. Remove (P1); System.out.println(set); System.out.println(set); set.add(new Person(1001, "CC")); System.out.println(set); set.add(new Person(1001, "AA")); System.out.println(set); } } class Person { int id; String name; public Person(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (! (o instanceof Person)) return false; Person person = (Person) o; if (id ! = person.id) return false; return name ! = null ? name.equals(person.name) : person.name == null; } @Override public int hashCode() { int result = id; result = 31 * result + (name ! = null ? name.hashCode() : 0); return result; } @Override public String toString() { return "Person{" + "id=" + id + ", name='" + name + '\'' + '}'; }}
Public class ChangeHashCode1 {public static void main(String[] args) {hashSet <Point> hs = new HashSet<Point>(); Point cc = new Point(); cc.setX(10); //hashCode = 41 hs.add(cc); cc.setX(20); System.out.println("hs. Remove = "+ hs. Remove (cc)); //false hs.add(cc); System.out.println("hs.size = " + hs.size()); //size = 2 System.out.println(hs); } } class Point { int x; public int getX() { return x; } public void setX(int x) { this.x = x; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() ! = obj.getClass()) return false; Point other = (Point) obj; if (x ! = other.x) return false; return true; } @Override public String toString() { return "Point{" + "x=" + x + '}'; }}

8.7. Cache leaks

Another common source of memory leaks is caching, which is easy to forget once you put an object reference in the cache. For example, during the first launch of a project, the application started up slowly and died because the code was loading a table of data into the cache (memory). The test environment only had a few hundred data items, but the production environment had millions of data items.

To address this problem, a WeakHashMap can be used to represent the cache, which is characterized by the fact that the Map automatically discards the value when it has no references to the key other than its own.

public class MapTest { static Map wMap = new WeakHashMap(); static Map map = new HashMap(); public static void main(String[] args) { init(); testWeakHashMap(); testHashMap(); } public static void init() { String ref1 = new String("obejct1"); String ref2 = new String("obejct2"); String ref3 = new String("obejct3"); String ref4 = new String("obejct4"); wMap.put(ref1, "cacheObject1"); wMap.put(ref2, "cacheObject2"); map.put(ref3, "cacheObject3"); map.put(ref4, "cacheObject4"); System.out.println("String references ref1, ref2, ref3, ref4 disappear "); } public static void TestWeakHashMap () {System.out.println("WeakHashMap before GC ");} public static void TestWeakHashMap () {System.out.println("WeakHashMap before GC "); for (Object o : wMap.entrySet()) { System.out.println(o); } try { System.gc(); TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("WeakHashMap after GC ");} System.out.println("WeakHashMap after GC "); for (Object o : wMap.entrySet()) { System.out.println(o); } public static void tesHashMap () {System.out.println("HashMap before GC ");} public static void tesHashMap () {System.out.println("HashMap before GC "); for (Object o : map.entrySet()) { System.out.println(o); } try { System.gc(); TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("HashMap after GC "); for (Object o : map.entrySet()) { System.out.println(o); }}}

The above code and diagram lead actor demonstrate WeakHashMap how to automatically release the cache object. When the init function is completed, local variable string reference Weakd1, Weakd2, D1 and D2 will disappear. At this time, only the reference to the string object saved in the static map can be seen. The HashMap is not recycled, while the cache in the WeakHashMap is recycled.

8.8. Listeners and other callbacks

The third common source of memory leaks is listeners and other callbacks, which accumulate if a client registers callbacks in the API you implement but does not explicitly cancel them.

The best way to ensure that a callback is immediately garbage collected is to keep only its weak references, such as saving them as keys in a WeakHashMap.

9. Memory leak case analysis

public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object E) {// Stack EnsureCapacity (); elements[size++] = e; } public Object pop() {if (size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}

There is nothing obviously wrong with the above program, but this program has a memory leak. As GC activity increases, or memory usage increases, the performance of the program degrades. In severe cases, memory leaks can occur, but these failures are relatively rare.

The main problem with the code is in the pop function, which is illustrated below. Suppose the stack keeps growing, as shown in the figure below

When performing a large number of POP operations, GC does not release the reference because it is not emptied, as shown in the figure below

As you can see from the figure above, if the stack grows first and then shrinks, objects that pop off the stack will not be garbage collected. Even if the program no longer uses the stack, they will not be garbage collected because the stack still holds a reference to the object, known as an expired reference. This memory leak is very hidden.

Change the pop() method in your code to the following method:

public Object pop() {
    if (size == 0)
        throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

Once the references have expired, clear them and leave them empty.