简体   繁体   English

try-resource-block中的AutoCloseable延迟执行

[英]AutoCloseable Delayed Execution in try-resource-block

I have noticed that the execution of the close method on AutoCloseable objects is sometimes delayed. 我注意到在AutoCloseable对象上执行close方法有时会延迟。 It's normally executed within a millisec or two, but sometimes it takes up to a hundred millisecs or more. 通常在一或两个毫秒内执行,但有时最多需要一百个毫秒或更多。

I am unable to find Java documentation describing the expected timing of try-with-resource. 我找不到描述try-with-resource的预期时间的Java文档。 Can someone help explain what why there is sometimes such a large delay? 有人可以帮忙解释一下为什么有时会有这么大的延迟吗?

Below is a test to show this. 下面是测试以证明这一点。 The results vary, but if ran enough, results begin to show delayed execution of the close method. 结果各不相同,但如果运行得足够,结果将开始显示close方法的执行延迟。

public class ClosableTest {
    public class TimerResource implements AutoCloseable {
        private Instant startTime;
        private long sleepTime;
        private long actualTime;

        public TimerResource() {
            startTime = Instant.now();
        }

        public void setSleepTime(long sleepTime) {
            this.sleepTime = sleepTime;
        }

        public void setActualTime(long actualTime) {
            this.actualTime = actualTime;
        }

        @Override
        public void close() throws Exception {
            long closeTime = Duration.between(startTime, Instant.now()).toMillis();
            //System.out.println(String.format("%s: Sleep time: %d; Actual time: %d; Closed time: %s", Thread.currentThread().getName(), sleepTime, actualTime, closeTime));
            if (closeTime > actualTime+5) {
                System.out.println("Close took more than 5ms");
                System.out.println(String.format("\t%s: Sleep time: %d; Actual time: %d; Closed time: %s", Thread.currentThread().getName(), sleepTime, actualTime, closeTime));
            }
        }
    }

    @Test
    public void timingTest() {
        Instant start;
        long realDuration;

        for (int i = 0; i < 100; i++) {
            try (TimerResource timer = new TimerResource()) {
                start = Instant.now();

                long sleepTime = 10L + (long) (400 * Math.random());
                timer.setSleepTime(sleepTime);
                Thread.sleep(sleepTime);

                realDuration = Duration.between(start, Instant.now()).toMillis();
                timer.setActualTime(realDuration);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

The close() is called immediately. close()被立即调用。 However when you try to determine this empirically, you are only as reliable as your benchmark. 但是,当您尝试凭经验确定这一点时,您只能像基准一样可靠。

BTW If you call System.nanoTime() in a tight loop, you can see jumps in time of 1 - 50 ms due to the process being rescheduled. 顺便说一句,如果在紧密循环中调用System.nanoTime() ,由于重新安排了进程,您会看到1到50毫秒的时间跳跃。 You can see this on your machine by running this test tools https://github.com/OpenHFT/Java-Thread-Affinity/blob/master/affinity/src/main/java/net/openhft/affinity/MicroJitterSampler.java 您可以通过运行以下测试工具在您的计算机上看到此信息:https://github.com/OpenHFT/Java-Thread-Affinity/blob/master/affinity/src/main/java/net/openhft/affinity/MicroJitterSampler.java

If you look at the byte code for this example. 如果您查看此示例的字节码。

public static void main(String... args) {
    try (PrintWriter pw = new PrintWriter(System.out)) {
        pw.println("Hello World");
    }
}

you can see that close is called immediately after println 您可以看到在println之后立即调用close

   L11
    LINENUMBER 13 L11
    NEW java/io/PrintWriter
    DUP
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    INVOKESPECIAL java/io/PrintWriter.<init> (Ljava/io/OutputStream;)V
    ASTORE 1
   L12
    ACONST_NULL
    ASTORE 2
   L3
    LINENUMBER 14 L3
    ALOAD 1
    LDC "Hello World"
    INVOKEVIRTUAL java/io/PrintWriter.println (Ljava/lang/String;)V
   L4
    LINENUMBER 15 L4
    ALOAD 1
    IFNULL L13
    ALOAD 2
    IFNULL L14
   L0
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintWriter.close ()V
   L1
    GOTO L13
   L2

For the sake of brevity I have removed the try/finally byte code which is not executed unless there is a Throwable. 为了简洁起见,我删除了try / final字节代码,除非存在Throwable,否则不会执行。

I don't believe there should be a significant difference between both cases. 我不认为这两种情况之间应该有significant差异。 As per java docs . 根据java docs Try with resource translate like below. Try with resource如下所示的Try with resource翻译。

Suppose your try with resource looks like 假设您尝试使用的资源看起来像

try ({VariableModifier} R Identifier = Expression ...)
    Block

Will translate to : 将转换为:

{
    final {VariableModifierNoFinal} R Identifier = Expression;
    Throwable #primaryExc = null;

    try ResourceSpecification_tail
        Block
    catch (Throwable #t) {
        #primaryExc = #t;
        throw #t;
    } finally {
        if (Identifier != null) {
            if (#primaryExc != null) {
                try {
                    Identifier.close();
                } catch (Throwable #suppressedExc) {
                    #primaryExc.addSuppressed(#suppressedExc);
                }
            } else {
                Identifier.close();
            }
        }
    }
}

So it is clear that try with resource and normal try-catch are nearly same . 因此很明显,使用资源进行尝试和正常try-catch nearly same The only difference is that you may put / or not all the checks generated during translation of the try with resource . 唯一的区别是,您可以/不使用try with resource翻译时生成的所有检查。

Can someone help explain what why there is sometimes such a large delay? 有人可以帮忙解释一下为什么有时会有这么大的延迟吗?

Sometimes large delay may be due to several reason like CPU usage at that time, available memory, GC etc. 有时,较大的延迟可能是由于多种原因造成的,例如当时的CPU使用率,可用内存,GC等。

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

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