我需要知道何时在finalize()
中调用该方法JVM
。我创建了一个测试类,该类finalize()
通过覆盖该方法在调用该方法时将其写入文件。它不执行。谁能告诉我它为什么不执行的原因?
finalize()
垃圾回收没有任何影响。
我需要知道何时在finalize()
中调用该方法JVM
。我创建了一个测试类,该类finalize()
通过覆盖该方法在调用该方法时将其写入文件。它不执行。谁能告诉我它为什么不执行的原因?
finalize()
垃圾回收没有任何影响。
Answers:
通常,最好不要依赖finalize()
进行任何清理等工作。
根据Javadoc(值得阅读),它是:
当垃圾回收确定不再有对该对象的引用时,由垃圾回收器在对象上调用。
正如Joachim指出的那样,如果始终可以访问对象,则在程序生命中可能永远不会发生这种情况。
同样,不保证垃圾收集器在任何特定时间运行。总的来说,我想说的finalize()
可能不是一般上最好的方法,除非您需要特定的东西。
finalize
有用呢?
finalize()
对主类方法中,当类的一个实例>> <<被垃圾回收,主要方法终止时不被调用。此外,可以在应用程序完成之前对主类进行垃圾回收。例如,在多线程应用程序中,“主”线程创建其他线程,然后返回。(在实践中,将需要非标准的类加载器...。)
finalize
当对象将要收集垃圾时调用该方法。在可以进行垃圾收集之后,可以随时进行。
请注意,对象很可能永远不会被垃圾回收(因此finalize
永远不会被调用)。当对象从不符合gc资格(因为在JVM的整个生命周期内都是可以访问的)时,或者在对象符合资格的时间与JVM停止运行的时间之间没有运行垃圾回收时,通常会发生这种情况。测试程序)。
有一些方法可以告诉JVM运行 finalize
在尚未被调用的对象,但是使用它们也不是一个好主意(该方法的保证也不是很强大)。
如果您依靠finalize
应用程序的正确操作,那么您做错了什么。finalize
应该仅用于清理(通常是非Java)资源。而这正是因为JVM没有这样的保证finalize
是曾经把任何物体上。
Closable
您可能需要接口(及其背后的想法):.close()
关闭/丢弃资源,并要求类的用户在正确的时间调用它。您可能想要添加一种finalize
“仅保存”的方法,但这将比实际的修复更多地是调试工具(因为它不够可靠)。
System.gc()
以调用FileInputStream :: finalize(),然后才能移动文件。
protected void finalize() throws Throwable {}
- 每个类都
finalize()
从java.lang.Object 继承方法- 当垃圾回收器确定不再存在对该对象的引用时,将调用该方法
- Object finalize方法不执行任何操作,但是可以被任何类覆盖
- 通常它应该被覆盖以清理非Java资源,即关闭文件
如果覆盖过多
finalize()
,则使用try-catch-finally语句并始终调用是一个很好的编程习惯super.finalize()
。这是一项安全措施,可确保您不会无意间错过了关闭由调用类的对象使用的资源的操作protected void finalize() throws Throwable { try { close(); // close open files } finally { super.finalize(); } }
finalize()
垃圾回收期间引发的任何异常都将终止最终确定,但否则将被忽略finalize()
在任何对象上都不会运行超过一次
引用自:http : //www.janeg.ca/scjp/gc/finalize.html
您还可以查看这篇文章:
请查看有效Java,第二版,第27页。 项目7:避免使用终结器
终结器是不可预测的,通常很危险,而且通常是不必要的。在终结器中永远不要做任何时间紧迫的事情。从不依赖终结器来更新关键的持久状态。
要终止资源,请改用try-finally:
// try-finally block guarantees execution of termination method Foo foo = new Foo(...); try { // Do what must be done with foo ... } finally { foo.terminate(); // Explicit termination method }
finalize()
Java 何时调用该方法?
在GC检测到对象不再可访问之后,并且在它实际回收该对象使用的内存之前,将调用finalize方法。
如果一个对象永远不会变得不可访问,finalize()
则永远不会对其进行调用。
如果GC不运行,则finalize()
可能永远不会调用。(通常,GC仅在JVM认为有足够的垃圾使它值得时才运行。)
GC可能需要一个以上的GC周期才能确定某个特定对象不可访问。(Java GC通常是“世代”的收集器...)
一旦GC检测到对象不可访问和可终结,则将其放置在终结队列中。完成通常与普通GC异步进行。
(JVM规范实际上允许 JVM 永远不运行终结器……只要不回收对象使用的空间即可。通过这种方式实现的JVM将会残废/无用,但是这种行为是“允许的” )
结果是,依靠终结来完成必须在确定的时间范围内完成的事情是不明智的。完全不使用它们是“最佳实践”。应该有一种更好(即更可靠)的方法来执行您正在尝试执行的操作finalize()
方法。
终结的唯一合法用途是清除与应用程序代码丢失的对象相关的资源。即使这样,您也应该尝试编写应用程序代码,以免它一开始就不会丢失对象。(例如,使用Java 7+ try-with-resources确保close()
始终被称为...)
我创建了一个测试类,当通过覆盖它调用finalize()方法时,该类将写入文件。它不执行。谁能告诉我它为什么不执行的原因?
很难说,但是有几种可能性:
由于由JVM调用finalize()方法存在不确定性(不确定是否会执行被覆盖的finalize()),出于研究目的,观察调用finalize()时发生的事情的更好方法是强制JVM通过命令调用垃圾回收 System.gc()
。
具体来说,当不再使用对象时,将调用finalize()。但是,当我们尝试通过创建新对象来调用它时,并不确定调用它。因此,对于把握我们创建了一个null
对象c
,这显然是没有前途的使用,因此我们看到物体c
的敲定电话。
例
class Car {
int maxspeed;
Car() {
maxspeed = 70;
}
protected void finalize() {
// Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
// Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection
System.out.println("Called finalize method in class Car...");
}
}
class Bike {
int maxspeed;
Bike() {
maxspeed = 50;
}
protected void finalize() {
System.out.println("Called finalize method in class Bike...");
}
}
class Example {
public static void main(String args[]) {
Car c = new Car();
c = null; // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it
Bike b = new Bike();
System.gc(); // should clear c, but not b
for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
System.out.print("\t" + b.maxspeed);
if (b.maxspeed > 50) {
System.out.println("Over Speed. Pls slow down.");
}
}
}
}
输出量
Called finalize method in class Car...
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51Over Speed. Pls slow down.
52Over Speed. Pls slow down.
53Over Speed. Pls slow down.
54Over Speed. Pls slow down.
55Over Speed. Pls slow down.
56Over Speed. Pls slow down.
57Over Speed. Pls slow down.
58Over Speed. Pls slow down.
59Over Speed. Pls slow down.
60Over Speed. Pls slow down.
61Over Speed. Pls slow down.
62Over Speed. Pls slow down.
63Over Speed. Pls slow down.
64Over Speed. Pls slow down.
65Over Speed. Pls slow down.
66Over Speed. Pls slow down.
67Over Speed. Pls slow down.
68Over Speed. Pls slow down.
69Over Speed. Pls slow down.
70Over Speed. Pls slow down.
注 –即使在打印多达70个并且在程序中未使用对象b之后,由于没有打印“类Bike ...中的被调用的finalize方法”,JVM也可能清除b或不清除b。
System.gc();
并不能保证垃圾回收将真正运行。
finalize将打印出用于创建类的计数。
protected void finalize() throws Throwable {
System.out.println("Run F" );
if ( checkedOut)
System.out.println("Error: Checked out");
System.out.println("Class Create Count: " + classCreate);
}
主要
while ( true) {
Book novel=new Book(true);
//System.out.println(novel.checkedOut);
//Runtime.getRuntime().runFinalization();
novel.checkIn();
new Book(true);
//System.runFinalization();
System.gc();
如你看到的。下面的输出显示了gc在类计数为36时第一次执行。
C:\javaCode\firstClass>java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F
最近为了解决测试过程中的finalizer方法(为了在测试过程中处置连接池),我不得不说finalizer缺少很多东西。使用VisualVM进行观察以及使用弱引用来跟踪实际的交互,我发现在Java 8环境(Oracle JDK,Ubuntu 15)中,以下几点是正确的:
最终思想
终结方法不可靠,但只能用于一件事。您可以确保在垃圾回收之前关闭或处置了一个对象,如果正确处理了涉及生命周期结束动作的更复杂生命周期的对象,则可以实现故障保护。这是我可以想到的一个原因,因此值得对其进行覆盖。
如果某个对象无法从任何活动线程或任何静态引用中访问,则该对象符合垃圾收集或GC的条件,换句话说,如果该对象的所有引用均为null,则该对象符合垃圾收集的条件。循环依赖性不算作引用,因此,如果对象A引用了对象B,而对象B引用了对象A,并且它们没有任何其他实时引用,则对象A和B都可以进行垃圾回收。通常,在以下情况下,对象可以使用Java进行垃圾回收:
final
在缓慢的计算过程中重复使用对象的字段,并且此后将不再使用该对象,则该对象会一直保持活动状态,直到源代码最后请求该字段为止,或者JIT可以将该字段复制到临时变量,然后在计算之前放弃对象?
GC.KeepAlive()
除了强制GC假定它可能使用对象之外,没有执行任何其他功能,但是我知道Java中没有这样的功能。可以将volatile
变量用于此目的,但仅将变量用于此目的似乎是浪费的。
FinalizerReference
,这样就不需要GC周期来找出没有参考。同步足以确保事前发生关系。由于完成可能(实际上是)在不同的线程中运行,因此无论如何在形式上通常都是必需的。Java 9将添加Reference.reachabilityFence
……
Finalize
会被调用的是JIT产生的代码直接这样做了。通常只希望在单个线程中使用的对象需要同步代码吗?如果对象执行某些需要放弃的操作(例如,打开套接字连接并获得另一端资源的独占使用权),那么在代码仍在使用套接字的情况下,让终结器关闭该连接将是一场灾难。 。代码使用同步会很正常吗?
有时,当对象被销毁时,必须采取行动。例如,如果对象具有非Java资源(例如文件句柄或字体),则可以在销毁对象之前验证是否释放了这些资源。为了管理这种情况,java提供了一种称为“完成”的机制。通过完成它,您可以定义在将要从垃圾收集器中删除对象时发生的特定操作。要将终结器添加到类中,只需定义finalize()方法。Java执行时将在要删除该类的对象时调用此方法。在finalize 方法内,即可指定销毁对象之前要执行的操作。定期在垃圾收集器中搜索不再引用任何运行状态或间接引用任何其他对象的对象。在释放资产之前,Java运行时会在对象上调用finalize()方法。的finalize()方法具有以下一般形式:
protected void finalize(){
// This is where the finalization code is entered
}
使用protected关键字,可以防止通过其类外部的代码访问finalize()。重要的是要了解,就在垃圾回收之前调用了finalize()。例如,当对象离开范围时不调用它。这意味着您不知道何时或是否将执行finalize()。结果,程序必须提供其他方式来释放系统资源或对象使用的其他资源。您不应该依赖finalize()正常运行程序。
覆盖finalize方法的类
public class TestClass {
public TestClass() {
System.out.println("constructor");
}
public void display() {
System.out.println("display");
}
@Override
public void finalize() {
System.out.println("destructor");
}
}
调用finalize方法的机会
public class TestGarbageCollection {
public static void main(String[] args) {
while (true) {
TestClass s = new TestClass();
s.display();
System.gc();
}
}
}
当内存中充满转储对象时,gc将调用finalize方法
运行并查看控制台,在这里您找不到频繁调用的finalize方法,当内存过载时,将调用finalize方法。
如https://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers中指出,
终结器没有固定的执行时间,因为执行时间取决于Java虚拟机(JVM)。唯一的保证是,任何执行的终结器方法都将在关联对象变得不可访问之后的某个时间(在垃圾回收的第一个周期中检测到)和垃圾回收器回收关联对象的存储之前的某个时间(在垃圾回收器的第二个周期期间)执行。 。在对象变得不可访问之后,对象的终结器的执行可能会延迟任意长时间。因此,调用时间紧迫的功能,例如在对象的finalize()方法中关闭文件句柄是有问题的。
尝试运行此程序以更好地理解
public class FinalizeTest
{
static {
System.out.println(Runtime.getRuntime().freeMemory());
}
public void run() {
System.out.println("run");
System.out.println(Runtime.getRuntime().freeMemory());
}
protected void finalize() throws Throwable {
System.out.println("finalize");
while(true)
break;
}
public static void main(String[] args) {
for (int i = 0 ; i < 500000 ; i++ ) {
new FinalizeTest().run();
}
}
}