When the program creates reference-type entities such as objects, arrays, etc., the system will allocate a piece of memory for the object in the heap memory, and the object is stored in this piece of memory. When this piece of memory is no longer referenced by any reference variable, the piece of memory becomes garbage, waiting for the garbage collection mechanism to be recycled. The garbage collection mechanism has three characteristics:
The garbage collection mechanism is only responsible for recycling objects in the heap memory, and will not recycle any physical resources (such as database connections, open file resources, etc.), nor will it recycle the memory allocated to the object in a way other than the way of creating an object, (such as memory applied for by the object calling malloc in the local method)
The program cannot accurately control the operation of garbage collection, so it can only be recommended to garbage collection. There are two recommended methods: System.gc() and Runtime.getRuntime().gc()
Before garbage collection, its finalize() method will always be called first, but it is the same as the garbage collection time, and the finalize() method is also not sure.
Regarding the above three characteristics, there are three problems:
1. The cleaning work must be done manually to free up memory and other physical resources allocated in a way other than the way of creating objects. And be careful to eliminate expired object references, otherwise OOM may be caused.
Manual cleaning usually uses a code structure like try... finally...
Examples are as follows:
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(); } } }}There are usually three common OOM cases caused by reference to expired objects. These three cases are usually not easy to detect, and there will be no problems running in a short period of time. However, after a long time, the number of leaked objects will eventually cause the program to crash.
When the class manages memory by itself, you should be wary of memory leaks as follows:
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(); } }}The reason for memory leaks is that even if other objects on the stack are no longer referenced, the elements[] array in the Stack class still holds references to these objects, resulting in the objects not being recycled by garbage collection. Therefore, when the class needs to manage memory by itself, beware of whether these expired references maintained by internally are dereferenced in time. In this example, only after the stack is released, the displayed one will be displayed.
elements[size] = null;
Cache is to be wary of memory leaks. This situation is usually the case that once the object is put into the cache, it is likely that it is easy to forget if it is not used for a long time. Usually, WakeHashMap can be used to represent the cache. After the items in the cache expire, they can be automatically deleted. Or it can be periodically executed by a background thread to clear expired items in the buffer.
Registration of listeners or callbacks is best displayed to unregister.
2. Do not call finalize() manually, it is called to the garbage collector
3. Avoid using the finalize() method unless it is used as a judging the end condition to find that the object has not been properly cleaned; it is used as a security network to clean up system resources when manually cleaned up forgot calls. Delayed cleaning should not be cleaned up. If you record the information about the forgotten cleaning resource at the same time, it is also convenient for errors to be discovered later and modify the forgotten cleaning code in time; free up the not-very critical system resources obtained by the local method in the object.
Since the finalize() method is not accurately ensured, it is best not to release key resources, 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 results:
Error: check out
The Book object in the example must be in the checkIn state before being released, otherwise it cannot be released. The implementation in finalize can help to discover illegal objects in time, or more directly, use a reference variable to directly refer to it in finalize, so that it can re-enter the state of reachable, and then process it again.
Another point to note is that if the subclass overrides the finalize method of the parent class, but forgets to manually call super.finalize or the finalize process of the subclass has an exception, resulting in super.finalize not being executed, then the end method of the parent class will never be called.
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 results:
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 results:
Son finalize start
For the second case, you can use the try... finally... structure to solve it, but for the first case, it is better to use a method called the end method guardian. The example is as follows
class Parent2{ private final Object finalizeGuardian = new Object() { protected void finalize() throws Throwable { System.out.println("Execute the logic in the parent class terminating 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 results:
Execute the logic in the parent class termination method here
Son2 finalize start
This ensures that the operations required in the end method of the parent class are executed.
The above is all about this article, I hope it will be helpful to everyone's learning.