我听说在 Joshua Bloch 写的书中,如果我们重写 finalize 方法,分配和内存收集可能会增加到 430 倍。
对我来说很明显,内存收集可能会更慢,因为 gc 需要额外的迭代来释放内存。
但是为什么分配阶段可以增加呢?
我听说在 Joshua Bloch 写的书中,如果我们重写 finalize 方法,分配和内存收集可能会增加到 430 倍。
对我来说很明显,内存收集可能会更慢,因为 gc 需要额外的迭代来释放内存。
但是为什么分配阶段可以增加呢?
我已经搜索了原始声明:
在我的机器上,创建和销毁一个简单对象的时间约为 5.6 ns。添加终结器会将时间增加到 2,400 ns。换句话说,使用终结器创建和销毁对象的速度大约慢了 430 倍。
所以这不是一个一般性的陈述,而只是一份证据报告,表明它背后有一个模式,而不是这个数字是可重现的。当使用不那么微不足道的对象或更多的对象时,这个因素可能会改变。
当然,这些成本取决于最终确定的实际实施方式。在 HotSpot 中,每次创建具有非平凡方法的对象时,Finalizer
都会通过调用该方法来创建一个实例。Finalizer.register
finalize()
这可能意味着比仅仅分配两个对象而不是一个对象要多得多的成本。这些Finalizer
实例将被强链接,这对于防止实例本身的集合是必要的Finalizer
,并且它们具有对构造对象的引用。换句话说,无论最初分配的对象有多局部,新对象都会逃逸,从而阻碍了许多后续的优化。
当谈到“破坏”时,回收一个普通的对象是无操作的。不会采取任何行动,事实上,不可能对无法访问的对象做任何事情,因为它是unreachable。特殊的可达性状态只能通过拥有一个可达Reference
对象来遇到,比如Finalizer
上面提到的对象,它持有对特定对象的引用(而对象不是通过任何其他普通引用遇到的。然后,Reference
对象可以入队,之后哪个(其中一个)终结器线程可以采取适当的行动。
当然,将“不采取行动”与任何其他行动进行比较可能会导致任意因素。绝对数字是 2,400ns,这对于涉及将对象入队并通知另一个线程轮询队列的操作是合理的。