Home > Java > javaTutorial > body text

Briefly understand Java's garbage collection mechanism and the role of the finalize method

高洛峰
Release: 2017-01-17 15:45:49
Original
1254 people have browsed it

When the garbage collector wants to recycle an object, it must first call the finalize method of this class (you can write a program to verify this conclusion). Generally, Class written in pure Java does not need to override this method because Object has already implemented a The default, unless we want to implement special functions (which involves many things, such as object space trees, etc.).
However, for Classes written in code other than Java (such as JNI, memory allocated by the new method of C++), the garbage collector cannot correctly recycle these parts. At this time, we need to override the default method to achieve the purpose. Correct release and recycling of this part of memory (for example, C++ requires delete).
In short, finalize is equivalent to the destructor. It is the first method to be called when the garbage collector recycles an object. However, since Java's garbage collection mechanism can automatically do these things for us, we generally do not need to manually release it ourselves.

Sometimes when undoing an object, some operations need to be completed. For example, if an object is handling non-Java resources, such as file handles or window character fonts, you will want to make sure that these resources are released before an object is destroyed. To handle such situations, Java provides a mechanism called finalization. Using this mechanism you can define special operations that are performed when an object is about to be released by the garbage collector.
To add a finalizer to a class, you only need to define the finalize () method. This method is called when Java recycles an object of this class. In the finalize() method, you specify the actions that must be performed before an object is destroyed. Garbage collection runs periodically, checking that objects are no longer referenced by runtime or indirectly through other objects. Just before the object is released, the Java runtime system calls the object's finalize() method.

The general format of the finalize() method is as follows:

  protected void finalize( )
{
// finalization code here
}
Copy after login

Among them, the keyword protected prevents code defined outside the class from accessing the finalize() identifier. This and other identifiers are explained in Chapter 7.

It is important to understand that finalize() is called just before garbage collection. For example, when an object exceeds its scope, finalize() is not called. This means you have no way of knowing when - or even if - finalize( ) is called. Therefore, your program should provide other methods to release system resources used by objects and cannot rely on finalize() to complete the normal operation of the program.

Note: If you are familiar with C++, you know that C++ allows you to define a undo function (destructor) for a class, which is called just before the object goes out of scope. Java does not support this idea nor provide an undo function. The finalize() method is only close to the functionality of the undo function. As you become more experienced with Java, you will see that there is little need to use the undo function because Java uses a garbage collection subsystem.


The working principle of finalize should be like this: once the garbage collector is ready to release the storage space occupied by the object, it first calls finalize(), and only during the next garbage collection process, will it really Reclaim the memory of the object. So if you use finalize(), you can perform some important cleaning or cleaning work during garbage collection.

When is finalize() called?
There are three situations

Automatically called when all objects are Garbage Collection, such as when running System.gc().

The finalize method is called once for each object when the program exits.

Explicitly call the finalize method

In addition, under normal circumstances, when an object is collected as useless information by the system, finalize() will be automatically called, but the jvm does not It is guaranteed that finalize() must be called, that is to say, the call of finalize() is uncertain, which is why sun does not advocate the use of finalize()

Sometimes when an object is revoked, it needs to be completed some operations. For example, if an object is handling non-Java resources, such as file handles or window character fonts, you will want to make sure that these resources are released before an object is destroyed. To handle such situations, Java provides a mechanism called finalization. Using this mechanism you can define special operations that are performed when an object is about to be released by the garbage collector.

To add a finalizer to a class, you only need to define the finalize () method. This method is called when Java recycles an object of this class. In the finalize() method, you specify the actions that must be performed before an object is destroyed. Garbage collection runs periodically, checking that objects are no longer referenced by the running state or indirectly through other objects. Just before the object is released, the Java runtime system calls the object's finalize( ) method.

The general format of the finalize() method is as follows:

protected void finalize( )
{
// finalization code here
}
Copy after login

The keyword protected prevents code defined outside the class from accessing the finalize() identifier. This identifier and other identifiers are explained in Chapter 7.

It is important to understand that finalize() is called just before garbage collection. For example, when an object exceeds its scope, finalize() is not called. This means you have no way of knowing when - or even if - finalize( ) is called. Therefore, your program should provide other methods to release system resources used by objects and cannot rely on finalize() to complete the normal operation of the program.

Note: If you are familiar with C++, you know that C++ allows you to define a undo function (destructor) for a class, which is called just before the object goes out of scope. Java does not support this idea nor provide an undo function. The finalize() method is only close to the functionality of the undo function. As you become more experienced with Java, you will see that there is little need to use the undo function because Java uses a garbage collection subsystem.

The garbage collector will automatically call the object's finalize method when collecting garbage to perform some user-defined non-memory cleanup work, because the garbage collector will not process things other than memory. Therefore, sometimes users need to define some cleaning methods, such as processing non-memory resources such as files and ports.

1. Overview of JVM's gc
  
 GC, the garbage collection mechanism, refers to the memory occupied by jvm used to release objects that are no longer used. The Java language does not require the JVM to have gc, nor does it specify how gc works. However, commonly used JVMs have GC, and most GCs use similar algorithms to manage memory and perform collection operations.
 
Only after fully understanding the garbage collection algorithm and execution process can its performance be effectively optimized. Some garbage collections are specialized for special applications. For example, real-time applications primarily focus on avoiding garbage collection interruptions, while most OLTP applications focus on overall efficiency. Once you understand the application workload and the garbage collection algorithms supported by the JVM, you can optimize and configure the garbage collector.
 
The purpose of garbage collection is to remove objects that are no longer used. The gc determines whether to collect an object by determining whether it is referenced by a live object. GC must first determine whether the object is ready for collection. Two commonly used methods are reference counting and object reference traversal.
 
 1.1. Reference Counting
  
 Reference counting stores the number of all references to a specific object. That is to say, when the application creates a reference and the reference goes out of scope, the jvm must increase or decrease the reference number appropriately. When the number of references to an object reaches 0, garbage collection can occur.
 
 1.2. Object reference traversal
  
Early JVMs used reference counting, and now most JVMs use object reference traversal. Object reference traversal starts from a set of objects and recursively determines reachable objects along each link throughout the object graph. If an object is not reachable from one (at least one) of these root objects, it is garbage collected. During the object traversal phase, the GC must remember which objects are reachable in order to delete unreachable objects. This is called marking objects.
 
In the next step, gc will delete unreachable objects. When deleting, some GCs simply scan the stack, delete unmarked unmarked objects, and release their memory to generate new objects. This is called sweeping. The problem with this approach is that the memory is divided into many small segments, which are not large enough for the new object, but the combination is large. Therefore, many GCs can reorganize objects in memory and compress them to form usable space.
 
To this end, gc needs to stop other activities. This approach means that all application-related work stops and only the gc runs. As a result, many mixed requests are added and dropped during the response time. In addition, more complex GCs are continuously added or run simultaneously to reduce or clear application interrupts. Some gcs use a single thread to complete this work, while others use multi-threading to increase efficiency.
  
2. Several garbage collection mechanisms
  
 2.1. Mark-sweep collector
  
  This collector first traverses the object graph and marks reachable objects, and then scans the stack to find unmarked objects and release their memory. This type of collector generally uses a single thread to work and stops other operations.
 
 2.2. Mark-Compact Collector
 
Sometimes also called Mark-Sweep-Compact Collector, it has the same marking phase as Mark-Sweep Collector. In the second phase, the marked object is copied to a new area of ​​the stack in order to compress the stack. This collector also stops other operations.
  
 2.3. Copying collector
  
  This kind of collector divides the stack into two domains, often called half space. Only half of the space is used each time, and new objects generated by the jvm are placed in the other half of the space. When gc runs, it copies reachable objects to the other half of the space, thus compressing the stack. This method is suitable for short-lived objects. Continuous copying of long-lived objects will lead to reduced efficiency.
 
 2.4. Incremental collector
  
The incremental collector divides the stack into multiple domains and only collects garbage from one domain at a time. This causes minor application disruption.
  
 2.5. Generational collector
  
 This kind of collector divides the stack into two or more domains to store objects with different lifetimes. New objects generated by jvm are generally placed in one of the fields. Over time, the surviving objects will acquire a useful life and be transferred to a longer-lived domain. Generational collectors use different algorithms for different domains to optimize performance.
  
 2.6. Concurrent collector
  
  The concurrent collector runs at the same time as the application. These collectors generally have to stop other operations at some point (such as compaction) to complete a specific task, but because other applications can perform other background operations, the actual time to interrupt other processing is greatly reduced.
 
 2.7. Parallel collector
  
Parallel collectors use some traditional algorithm and use multiple threads to perform their work in parallel. Using multi-threading technology on multi-CPU machines can significantly improve the scalability of Java applications.

3. Object destruction process

在对象的销毁过程中,按照对象的finalize的执行情况,可以分为以下几种,系统会记录对象的对应状态:
unfinalized 没有执行finalize,系统也不准备执行。
finalizable 可以执行finalize了,系统会在随后的某个时间执行finalize。
finalized 该对象的finalize已经被执行了。

GC怎么来保持对finalizable的对象的追踪呢。GC有一个Queue,叫做F-Queue,所有对象在变为finalizable的时候会加入到该Queue,然后等待GC执行它的finalize方法。

这时我们引入了对对象的另外一种记录分类,系统可以检查到一个对象属于哪一种。
reachable 从活动的对象引用链可以到达的对象。包括所有线程当前栈的局部变量,所有的静态变量等等。
finalizer-reachable 除了reachable外,从F-Queue可以通过引用到达的对象。
unreachable 其它的对象。

来看看对象的状态转换图。

Briefly understand Javas garbage collection mechanism and the role of the finalize method

好大,好晕,慢慢看。

1 首先,所有的对象都是从Reachable+Unfinalized走向死亡之路的。

2 当从当前活动集到对象不可达时,对象可以从Reachable状态变到F-Reachable或者Unreachable状态。

3 当对象为非Reachable+Unfinalized时,GC会把它移入F-Queue,状态变为F-Reachable+Finalizable。

4 好了,关键的来了,任何时候,GC都可以从F-Queue中拿到一个Finalizable的对象,标记它为Finalized,然后执行它的finalize方法,由于该对象在这个线程中又可达了,于是该对象变成Reachable了(并且Finalized)。而finalize方法执行时,又有可能把其它的F-Reachable的对象变为一个Reachable的,这个叫做对象再生。

5 当一个对象在Unreachable+Unfinalized时,如果该对象使用的是默认的Object的finalize,或者虽然重写了,但是新的实现什么也不干。为了性能,GC可以把该对象之间变到Reclaimed状态直接销毁,而不用加入到F-Queue等待GC做进一步处理。

6 从状态图看出,不管怎么折腾,任意一个对象的finalize只至多执行一次,一旦对象变为Finalized,就怎么也不会在回到F-Queue去了。当然没有机会再执行finalize了。

7 当对象处于Unreachable+Finalized时,该对象离真正的死亡不远了。GC可以安全的回收该对象的内存了。进入Reclaimed。


对象重生的例子

class C { 
  static A a; 
} 
  
class A { 
  B b; 
  
  public A(B b) { 
    this.b = b; 
  } 
  
  @Override
  public void finalize() { 
    System.out.println("A finalize"); 
    C.a = this; 
  } 
} 
  
class B { 
  String name; 
  int age; 
  
  public B(String name, int age) { 
    this.name = name; 
    this.age = age; 
  } 
  
  @Override
  public void finalize() { 
    System.out.println("B finalize"); 
  } 
  
  @Override
  public String toString() { 
    return name + " is " + age; 
  } 
} 
  
public class Main { 
  public static void main(String[] args) throws Exception { 
    A a = new A(new B("allen", 20)); 
    a = null; 
  
    System.gc(); 
    Thread.sleep(5000); 
    System.out.println(C.a.b); 
  } 
}
Copy after login

期待输出

A finalize 
B finalize 
allen is 20
Copy after login

但是有可能失败,源于GC的不确定性以及时序问题,多跑几次应该可以有成功的。详细解释见文末的参考文档。

    3.1对象的finalize的执行顺序

所有finalizable的对象的finalize的执行是不确定的,既不确定由哪个线程执行,也不确定执行的顺序。 
考虑以下情况就明白为什么了,实例a,b,c是一组相互循环引用的finalizable对象。

    3.2何时及如何使用finalize

从以上的分析得出,以下结论。 
(1) 最重要的,尽量不要用finalize,太复杂了,还是让系统照管比较好。可以定义其它的方法来释放非内存资源。 
(2) 如果用,尽量简单。 
(3) 如果用,避免对象再生,这个是自己给自己找麻烦。 
(4) 可以用来保护非内存资源被释放。即使我们定义了其它的方法来释放非内存资源,但是其它人未必会调用该方法来释放。在finalize里面可以检查一下,如果没有释放就释放好了,晚释放总比不释放好。 
(5) 即使对象的finalize已经运行了,不能保证该对象被销毁。要实现一些保证对象彻底被销毁时的动作,只能依赖于java.lang.ref里面的类和GC交互了。 

更多Briefly understand Javas garbage collection mechanism and the role of the finalize method相关文章请关注PHP中文网!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!