1. Overview
1.概述
2. Destructor in Java
2.Java中的析构器
Every time we create an object, Java automatically allocates the memory on the heap. Similarly, whenever an object is no longer needed, the memory will automatically be deallocated.
每次我们创建一个对象时,Java都会自动在堆上分配内存。同样,每当一个对象不再需要时,内存就会自动被取消分配。
In languages like C, when we finish using an object in memory, we have to deallocate it manually. Unfortunately, Java doesn’t support manual memory deallocation. Moreover, one of the features of the Java programming language is taking care of object destruction by itself – using a technique called garbage collection.
在C语言中,当我们使用完内存中的一个对象时,我们必须手动去分配它。不幸的是,Java不支持手动去分配内存。此外,Java编程语言的一个特点是自行处理对象的销毁–使用一种叫做garbage集合的技术。
3. Garbage Collection
3.垃圾收集
Garbage collection removes unused objects from memory on the heap. It helps prevent memory leaks. Simply put, when there are no more references to the specific object and the object is no longer accessible, the garbage collector marks this object as unreachable and reclaims its space.
垃圾收集将未使用的对象从堆上的内存中移除。它有助于防止内存泄漏。简单地说,当不再有对特定对象的引用,并且该对象不再可以被访问时,垃圾收集器将该对象标记为不可访问,并回收其空间。
Failure to handle garbage collection properly can cause performance problems, and eventually, it causes an application to run out of memory.
如果不能正确处理垃圾收集,就会导致性能问题,最终导致应用程序的内存耗尽。
An object can be garbage collected when it reaches a state of no longer being accessible in the program. An object is no longer reachable when one of two situations occurs:
当一个对象达到在程序中不再可以访问的状态时,就可以进行垃圾回收。当两种情况之一发生时,一个对象就不再可及了。
- The object doesn’t have any references pointing to it
- All references to the object have gone out of scope
Java includes the System.gc() method to help support garbage collection. By calling this method, we can suggest to the JVM to run the garbage collector. However, we cannot guarantee the JVM will actually call it. The JVM is free to ignore the request.
Java包括System.gc()方法以帮助支持垃圾收集。通过调用这个方法,我们可以向JVM建议运行垃圾收集器。然而,我们不能保证JVM真的会调用它。JVM可以自由地忽略这个请求。
4. Finalizer
4.终结者
The Object class provides the finalize() method. Before the garbage collector removes an object from memory, it’ll call the finalize() method. The method can run zero or one time. However, it cannot run twice for the same object.
对象类提供了finalize()方法。在垃圾收集器从内存中删除一个对象之前,它会调用finalize()方法。该方法可以运行零次或一次。但是,对于同一个对象,它不能运行两次。
The finalize() method defined inside the Object class doesn’t perform any special action.
定义在Object类内的finalize()方法并不执行任何特殊的动作。
The main goal of the finalizer is to release resources used by the object before its removal from the memory. For instance, we can override the method to close the database connections or other resources.
终结者的主要目标是在对象从内存中移除之前释放其使用的资源。例如,我们可以覆盖该方法来关闭数据库连接或其他资源。
Let’s create a class that contains the BufferedReader instance variable:
让我们创建一个包含BufferedReader实例变量的类。
class Resource {
final BufferedReader reader;
public Resource(String filename) throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
}
public long getLineNumber() {
return reader.lines().count();
}
}
@Override
protected void finalize() {
try {
reader.close();
} catch (IOException e) {
// ...
}
}
When JVM calls the finalize() method, the BufferedReader resource will be released. The exceptions thrown by the finalize() method will stop the object finalization.
当JVM调用finalize()方法时,BufferedReader资源将被释放。由finalize()方法抛出的异常将停止对象的最终化。
However, since Java 9, the finalize() method has become deprecated. Using finalize() method can be confusing and hard to use properly.
然而,自Java 9以来,finalize()方法已被废弃。使用finalize()方法会让人感到困惑,而且很难正确使用。
If we want to release resources held by an object, we should consider implementing the AutoCloseable interface instead. Classes like Cleaner and PhantomReference provide a more flexible way to manage resources once an object becomes unreachable.
如果我们想释放一个对象所持有的资源,我们应该考虑实现AutoCloseable接口来代替。像Cleaner和PhantomReference这样的类提供了一种更灵活的方式来管理资源,一旦一个对象变得无法到达。
4.1. Implementing AutoCloseable
4.1.实现AutoCloseable
The AutoCloseable interface provides the close() method, which will be executed automatically when exiting a try-with-resources block. Inside this method, we can close resources used by an object.
AutoCloseable接口提供了close()方法,它将在退出try-with-resources块时自动执行。在这个方法中,我们可以关闭一个对象所使用的资源。
Let’s modify our example class to implement the AutoCloseable interface:
让我们修改我们的示例类以实现AutoCloseable接口。
class Resource implements AutoCloseable {
final BufferedReader reader;
public Resource(String filename) throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
}
public long getLineNumber() {
return reader.lines().count();
}
@Override
public void close() throws Exception {
reader.close();
}
}
We can use the close() method to close our resources instead of using the finalize() method.
我们可以使用close()方法来关闭我们的资源,而不是使用finalize()方法。
4.2. Cleaner Class
4.2.清洁器类
We can use the Cleaner class if we want to perform specific actions when an object becomes phantom reachable. In other words, when an object becomes finalized and its memory is ready to be deallocated.
如果我们想在一个对象成为幽灵可达时执行特定的动作,我们可以使用Cleaner类。换句话说,当一个对象变得最终化并且它的内存可以被分配时。
Now, let’s see how to use the Cleaner class. Firstly, let’s define Cleaner:
现在,让我们看看如何使用Cleaner类。首先,让我们定义Cleaner。
Cleaner cleaner = Cleaner.create();
Next, we’ll create a class that contains a cleaner reference:
接下来,我们将创建一个包含清洁器引用的类。
class Order implements AutoCloseable {
private final Cleaner cleaner;
public Order(Cleaner cleaner) {
this.cleaner = cleaner;
}
}
Secondly, we’ll define a static inner class that implements Runnable inside the Order class:
其次,我们将在Order类中定义一个实现Runnable的静态内类。
static class CleaningAction implements Runnable {
private final int id;
public CleaningAction(int id) {
this.id = id;
}
@Override
public void run() {
System.out.printf("Object with id %s is garbage collected. %n", id);
}
}
Instances of our inner class will represent cleaning actions. We should register each cleaning action in order for them to run after an object becomes phantom reachable.
我们内部类的实例将代表清洁行动。我们应该注册每一个清洁动作,以便它们在一个对象成为幻影可及之后能够运行。
We should consider not using a lambda for the cleaning action. By using a lambda, we could easily capture the object reference, preventing an object from becoming phantom reachable. Using a static nested class, as above, will avoid keeping the object reference.
我们应该考虑不为清洁动作使用lambda。通过使用lambda,我们可以很容易地捕捉到对象的引用,防止一个对象成为幻象可及。使用静态嵌套类,如上所述,将避免保留对象引用。
Let’s add the Cleanable instance variable inside the Order class:
让我们在Order类中添加Cleanable实例变量。
private Cleaner.Cleanable cleanable;
The Cleanable instance represents the cleaning object that contains the cleaning action.
Cleanable实例代表包含清洁动作的清洁对象。
Next, let’s create a method that will register the cleaning action:
接下来,让我们创建一个方法来注册清洁动作。
public void register(Product product, int id) {
this.cleanable = cleaner.register(product, new CleaningAction(id));
}
Finally, let’s implement the close() method:
最后,我们来实现close()方法。
public void close() {
cleanable.clean();
}
The clean() method unregisters the cleanable and invokes registered cleaning actions. This method will be called at most once regardless of the number of calls to clean.
clean()方法取消了对可清理对象的注册,并调用了注册的清理动作。无论对清洁的调用次数是多少,这个方法最多只会被调用一次。
When we use our CleaningExample instance inside a try-with-resources block, the close() method calls the cleaning action:
当我们在一个try-with-resources块中使用我们的CleaningExample实例时,close()方法会调用清洁动作。
final Cleaner cleaner = Cleaner.create();
try (Order order = new Order(cleaner)) {
for (int i = 0; i < 10; i++) {
order.register(new Product(i), i);
}
} catch (Exception e) {
System.err.println("Error: " + e);
}
In other cases, the cleaner will call the clean() method when an instance becomes phantom reachable.
在其他情况下,当一个实例成为幽灵可达时,清洁器将调用clean()方法。
Additionally, the behavior of cleaners during the System.exit() is implementation-specific. Java provides no guarantees whether cleaning actions will be invoked or not.
此外,清洁器在System.exit()期间的行为是特定的实现。Java不保证清洁动作是否会被调用。