简体   繁体   English

计算对象从创建到垃圾收集的总时间

[英]Calculate total time an object lasted from its creation to garbage collection

In Effective Java 3rd edition, on page 50, author has talked about total time an object lasted from its creation to the time it was garbage collected. 在Effective Java 3rd edition(第50页)中,作者讨论了一个对象从创建到垃圾收集的总时间。

On my machine, the time to create a simple AutoCloseable object, to close it using try-with-resources, and to have the garbage collector reclaim it is about 12 ns. 在我的机器上,创建一个简单的AutoCloseable对象,使用try-with-resources关闭它,并让垃圾收集器回收它的时间大约是12 ns。 Using a finalizer instead increases the time to 550 ns. 使用终结器将时间增加到550 ns。

How can we calculate such time? 我们怎样计算这个时间? Is there some reliable mechanism for calculating this time? 是否有一些可靠的机制来计算这个时间?

The only reliable method I am aware of ( I being emphasized here) is in java-9 via the Cleaner API , something like this: 我所知道的唯一可靠方法( 在这里强调)是通过Cleaner APIjava-9 ,如下所示:

static class MyObject {
    long start;
    public MyObject() {
        start = System.nanoTime();
    }
}

private static void test() {
    MyObject m = new MyObject();

    Cleaner c = Cleaner.create();
    Cleanable clean = c.register(m, () -> {
        // ms from birth to death 
        System.out.println("done" + (System.nanoTime() - m.start) / 1_000_000);
    });
    clean.clean();
    System.out.println(m.hashCode());
}

The documentation for register says: register文件说:

Runnable to invoke when the object becomes phantom reachable 当对象变为幻像可达时,可以调用Runnable

And my question was really what is phantom reachable after all? 而我的问题究竟是什么,幻影可以达到? (It's a question I still doubt I really understand it) (这是一个我仍然怀疑我真正理解的问题)

In java-8 the documentation says (for PhantomReference ) 在java-8文档中说(对于PhantomReference

Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. 与软引用和弱引用不同,垃圾收集器在排队时不会自动清除幻像引用。 An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable. 通过幻像引用可访问的对象将保持不变,直到所有此类引用都被清除或自身无法访问。

There are good topics here on SO that try to explain why this is so, taking into consideration that PhantomReference#get will always return null , thus not much use not to collect them immediately. 这里有很好的主题,试图解释为什么会这样,考虑到PhantomReference#get将始终返回null ,因此没有太多用于不立即收集它们。

There are also topics here (I'll try to dig them up), where it is shown how easy is to resurrect and Object in the finalize method (by making it strongly reachable again - I think this was not intended by the API in any way to begin with). 这里还有一些主题(我将尝试将它们挖掘出来),其中显示了在finalize方法中复活和对象是多么容易(通过使其再次强烈可达 - 我认为这不是API的任何目的)开始的方式)。

In java-9 that sentence in bold is removed, so they are collected. java-9 ,删除了粗体句子,因此它们收集。

Any attempt to track the object's lifetime is invasive enough to alter the result significantly. 任何跟踪对象生命周期的尝试都具有足够的侵入性,可以显着改变结果。

That's especially true for the AutoCloseable variant, which may be subject to Escape Analysis in the best case, reducing the costs of allocation and deallocation close to zero. 对于AutoCloseable变体尤其如此,在最佳情况下可能会受到Escape Analysis的影响,从而将分配和解除分配的成本降低到接近零。 Any tracking approach implies creating a global reference which will hinder this optimization. 任何跟踪方法都意味着创建一个阻碍此优化的全局引用。

In practice, the exact time of deallocation is irrelevant for ordinary objects (ie those without a special finalize() method). 实际上,解除分配的确切时间与普通对象(即没有特殊finalize()方法的对象无关)。 The memory of all unreachable objects will be reclaimed en bloc the next time the memory manager actually needs free memory. 下次内存管理器实际需要空闲内存时,将全部回收所有无法访问的对象的内存。 So for real life scenarios, there is no sense in trying to measure a single object in isolation. 因此,对于现实生活场景,尝试单独测量单个对象是没有意义的。

If you want to measure the costs of allocation and deallocation in a noninvasive way that tries to be closer to a real application's behavior, you may do the following: 如果您想以非侵入性方式测量分配和释放的成本,试图更接近真实应用程序的行为,您可以执行以下操作:

  • Limit the JVM's heap memory to n 将JVM的堆内存限制为n
  • Run a test program that allocates and abandons a significant number of the test instances, such, that their required amount of memory is orders of magnitude higher than the heap memory n . 运行一个测试程序,分配并放弃大量的测试实例,例如,它们所需的内存量比堆内存n高几个数量级。
  • measure the total time needed to execute the test program and divide it by the number of objects it created 测量执行测试程序所需的总时间,并将其除以创建的对象数

You know for sure that objects not fitting into the limited heap must have been reclaimed to make room for newer objects. 您肯定知道必须回收不适合有限堆的对象以便为较新的对象腾出空间。 Since this doesn't apply to the last allocated objects, you know that you have a maximum error matching the number of objects fitting into n . 由于这不适用于最后分配的对象,因此您知道最大错误与适合n的对象数相匹配。 When you followed the recipe and allocated large multiples of that number, you have a rather small error, especially when comparing the numbers reveals something like variant A needing ~12 ns per instance on average and variant B needing 550 ns (as already stated here , these numbers are clearly marked with “on my machine” and not meant to be reproducible exactly). 当您按照配方并分配了该数字的大倍数时,您会得到一个相当小的错误,特别是在比较数字时会发现类似于变量A的内容,平均每个实例需要~12 ns,而变量B需要550 ns(如此处所述 ,这些数字清楚地标有“在我的机器上”,并不意味着可以完全重现。

Depending on the test environment, you may even have to slow down the allocating thread for the variant with finalize() , to allow the finalizer thread to catch up. 根据测试环境,您甚至可能必须使用finalize()减慢变量的分配线程,以允许终结器线程赶上。
That's a real life issue, when only relying on finalize() , allocating too many resources in a loop can break the program. 这是一个现实生活中的问题,当只依赖于finalize() ,在循环中分配太多资源会破坏程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM