Java garbage collection and java garbage collection
When a program creates objects, arrays, and other referenced objects, the system allocates a memory for the object in the heap memory, and the object is saved in the memory, when the memory is no longer referenced by any reference variable, the memory will become garbage and wait for the garbage collection mechanism to recycle it. The garbage collection mechanism has three features:
- The garbage collection mechanism only recycles objects in the heap memory and does not recycle any physical resources (such as database connections and opened file resources ), it also does not recycle the memory allocated to the object in a way other than creating an object (for example, the memory applied for by calling the malloc method in the local method of the object)
- The program cannot accurately control the running of garbage collection. You can only recommend garbage collection. The recommended methods are System. gc () and Runtime. getRuntime (). gc ()
- Before garbage collection, the finalize () method of an object is always called. However, the method is the same as that of garbage collection, and the time to call the finalize () method is not fixed.
For the above three features, there are three problems:
1. Manual cleanup is required to release the memory and other physical resources allocated in addition to the object creation method. Also, eliminate expired object references. Otherwise, OOM may occur.
Manual cleanup usually uses code structures such as try... finally.
Example:
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class ManualClear { public static void main(String[] args) { FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream("./src/ManualClear.java"); } catch (FileNotFoundException e) { System.out.println(e.getMessage()); e.printStackTrace(); return; } try { byte[] bbuf = new byte[1024]; int hasRead = 0; try { while ((hasRead = fileInputStream.read(bbuf)) > 0) { System.out.println(new String(bbuf, 0, hasRead)); } } catch (IOException e) { e.printStackTrace(); } } finally { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }}
OOM is usually caused by the reference of expired objects. These three situations are usually not easy to find, and there will be no problems during a short period of time, but after a long time, when the leaked object is added, the program will eventually crash.
- When you manage your own memory, be cautious about memory leakage.
Example:
import java.util.Arrays;import java.util.EmptyStackException;class Stack{ private Object[] elements; private int size; private static final int DEFAULT_INITAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITAL_CAPACITY]; } public void push(Object e){ 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); } }}public class StackDemo { public static void main(String[] args) { Stack stack = new Stack(); for (int i = 0; i < 10000; i++) { stack.push(new Object()); } for(int i = 0; i < 10000; i++) { stack.pop(); } }}
Memory leakage occurs because the elements [] array in the Stack class still stores references to the objects even if other objects in the program are not referenced, as a result, these objects will not be recycled by garbage collection. Therefore, when you need to manage the memory by yourself, be cautious about whether these expired references maintained internally are promptly removed, in this example,
Elements [size] = null.
- Cache is to guard against Memory leakage
In this case, once an object is put into the cache, it may be easy to forget if it is not used for a long time. Generally, WakeHashMap can be used to represent the cache. After the items in the cache expire, they can be automatically deleted. Alternatively, a backend thread can regularly execute to clear expired items in the buffer.
- Listener or callback registration, it is best to display the cancellation of registration.
2. Do not manually call finalize (). It is called by the garbage collector.
3. Do not use the finalize () method unless it is used as an end condition to identify parts of the object that are not properly cleaned; it is used as a security net to clean up system resources when you forget to call it manually. If you delay cleaning, you should never clear the resources. If you record information about the resources that you forgot to clear at the same time, it also facilitates subsequent error discovery and timely modification of the code that you forget to clean up; releasing the local method in the object does not obtain critical system resources.
The finalize () method does not need to release key resources because its execution time and whether it is determined to be executed are not accurate, but can be used in the three cases mentioned above. The first case is as follows:
class Book { boolean checkout = false; public Book(boolean checkout) { this.checkout = checkout; } public void checkin(){ checkout = false; } @Override protected void finalize() throws Throwable { if (checkout) { System.out.println("Error: check out"); } }}public class FinalizeCheckObjectUse { public static void main(String[] args) { new Book(true); System.gc(); }}
Execution result:
Error: check out
In this example, the Book object must be in the checkIn State before it is released; otherwise, it cannot be released. The implementation in finalize can help to promptly discover illegal objects, or more directly, in finalize, a reference variable is directly used to re-enter the reachable state, and then process it again.
In addition, if the subclass overwrites the finalize method of the parent class, but forgets to manually call super. an exception occurred in the finalize process of the finalize or subclass, leading to the failure to execute super. in finalize, the termination method of the parent class will never be adjusted.
As follows:
class Parent{ @Override protected void finalize() throws Throwable { System.out.println(getClass().getName() + " finalize start"); }}class Son extends Parent{ @Override protected void finalize() throws Throwable { System.out.println(getClass().getName() + " finalize start"); }}public class SuperFinalizeLost { public static void main(String[] args) { new Son(); System.gc(); }}
Running result:
Son finalize start
Or
class Parent{ @Override protected void finalize() throws Throwable { System.out.println(getClass().getName() + " finalize start"); }}class Son extends Parent{ @Override protected void finalize() throws Throwable { System.out.println(getClass().getName() + " finalize start"); int i = 5 / 0; super.finalize(); }}public class SuperFinalizeLost { public static void main(String[] args) { new Son(); System.gc(); }}
Execution result:
Son finalize start
In the second case, try... finally... structure can be used to solve the problem. But in the first case, it is best to use an endpoint guardian method. Example:
Class Parent2 {private final Object finalizeGuardian = new Object () {protected void finalize () throws Throwable {System. out. println ("executing the logic in the parent class termination method here") ;};} class Son2 extends Parent2 {@ Override protected void finalize () throws Throwable {System. out. println (getClass (). getName () + "finalize start"); int I = 5/0; super. finalize () ;}} public class FinalizeGuardian {public static void main (String [] args) {new Son2 (); System. gc ();}}
Execution result:
Execute the logic Son2 finalize start in the parent class termination method here
This ensures that the operations required in the termination method of the parent class are executed.