1. An overview of the
An important feature of the Java language is the automatic collection and collection of the garbage collector without requiring us to manually manage and free memory, which makes Java memory leaks more difficult to detect and deal with.
If your program throws the Exception in the thread “is the main” Java. Lang. OutOfMemoryError: Java heap space, so this is usually caused because of memory leaks.
2. What is memory leak
In general, the principle of releasing an object is that it will never be used again. Assigning an object to NULL or any other object makes the space to which the object was originally referred inaccessible and thus unusable for GC collection. A memory leak is an object whose memory is no longer being used, but which is not reclaimed by the JVM.
- In general, memory leaks are likely if long-lived objects hold references to short-lived objects
3. Memory leakage caused by excessive scope
3.1. Problem description
public class Simple {
private Object object;
public void method() { object = new Object(); / /... }}Copy the code
In the above code, we assign the instantiated value of the class member variable object in the method method, but if we only use object in this method, it means that the space occupied by object will not be used again for the entire life of the class. This causes memory leaks, especially if object is a container with many elements added.
3.2 improve
The above improvements to the memory leak code are relatively simple.
public class Simple {
private Object object;
public void method() { object = new Object(); // Use the business code object = null; }}Copy the code
The rule for resolving memory leaks is to release references as soon as the object is no longer in use. Therefore, after the execution of the business code, the object object is no longer in use, and the value is assigned to null. Releasing its references allows the JVM to reclaim the corresponding memory.
Below is a section of jdK8 LinkedList source code.
E unlink(Node<E> x) {final E element = x.tem; final Node<E> next = x.next; final Node<E> prev = x.prev;if(prev == null) {// If the previous node is empty (such as the first node of the current node), the next node becomes the new first node. }else{// If the previous node is not empty, then it points to the current next node. x.prev = null; }if(next == null) {// If the next node is empty (e.g. the current node is the last node), the last node before the current node becomes the new last node; }else{// If the next node is not empty, the next node points forward to the current previous node next-.prev = prev; x.next = null; } x.item = null; size--; modCount++;return element;
}
Copy the code
As you can see, the members of X, next, item, and prev, are explicitly assigned null at the end of their use, so that they cannot be reclaimed by the JVM and can easily be ignored in real development.
4. Memory corruption caused by container elements
4.1. Problem description
Here is a POP method we implemented with ArrayList.
public E pop() {if(size == 0)
return null;
else
return (E) elementData[--size];
}
Copy the code
The implementation is simple, but there is a memory leak, because the size is smaller so that the end elements of the ArrayList are never used, but because the container holds their references, they are never released.
4.2 improve
public E pop() {if(size == 0)
return null;
else{
E e = (E) elementData[--size];
elementData[size] = null;
returne; }}Copy the code
The corresponding space is reclaimed by actively assigning null to free references to the corresponding element.
5. Memory leaks caused by the container itself
5.1. Problem description
Vector vec = new Vector();
for(int i = 1; i < 100; i++) { Object obj = new Object(); vec.add(obj); Obj = null; } // Use the relevant business logic of VECCopy the code
The above code is a very classic example. At first glance, there seems to be no problem. After each use of an element, the element reference is set to NULL, ensuring that the object space is reclaimed. However, the fact that the container itself takes up a very large amount of memory as it grows is often overlooked, and if the container itself is not null, the container itself will always live in scope.
5.2 improve
Vector vec = new Vector();
for(int i = 1; i < 100; i++) { Object obj = new Object(); vec.add(obj); Obj = null; } // use vec related business logic vec = null;Copy the code
The improvement is simple; it is always correct to assign null immediately when the container is no longer in use.
6. Memory leak caused by default EQUALS method used by Set/Map containers
6.1. Problem description
public class TestClass implements Cloneable {
private Long value;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class MainClass {
public Set<TestClass> method(List<TestClass> testList)
throws CloneNotSupportedException {
Set<TestClass> result = new HashSet<>();
for (int a = 0; a < 100000) {
for (TestClass test : testList) { result.add(test.clone()); }}}}Copy the code
It appears that the code above implements the logic of de-duplicating the testList passed in, and although it is repeated many, many times, our de-duplicating code does not waste any additional space. In fact, the clone and new operations reallocate space in memory, which means that their addresses are different. All classes inherit Object, so their equals methods are derived from the Object class. The default implementation is to return the address of the Object. Therefore, even though the clone objects are de-duplicated in the Set, they are still considered to be different objects, resulting in OutOfMemoryError being thrown.
6.2 improve
The improvement is as simple as adding the appropriate equals method implementation required for your custom class.
public class TestClass implements Cloneable {
private Long value;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object obj) {
return Objects.equals(obj.value, value);
}
}
public class MainClass {
public Set<TestClass> method(List<TestClass> testList)
throws CloneNotSupportedException {
Set<TestClass> result = new HashSet<>();
for (int a = 0; a < 100000) {
for (TestClass test : testList) { result.add(test.clone()); }}}}Copy the code
An ounce of prevention is worth a pound of cure