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

时间:2018-04-20 15:35:52

标签: java performance

我注意到在AutoCloseable对象上执行close方法有时会延迟。它通常在一两毫秒内执行,但有时需要一百毫秒或更多。

我无法找到描述try-with-resource预期时间的Java文档。有人可以帮助解释为什么有时会出现如此大的延迟吗?

以下是显示此问题的测试。结果有所不同,但如果运行得足够多,结果会开始显示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();
            }
        }
    }
}

2 个答案:

答案 0 :(得分:3)

立即调用close()。但是,当您尝试根据经验确定时,您只能像基准一样可靠。

BTW如果你在紧密循环中调用System.nanoTime(),由于重新安排了进程,你可以看到1到50毫秒的跳跃。您可以通过运行此测试工具https://github.com/OpenHFT/Java-Thread-Affinity/blob/master/affinity/src/main/java/net/openhft/affinity/MicroJitterSampler.java

在计算机上看到此信息

如果查看此示例的字节代码。

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

你可以看到在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

为了简洁起见,我删除了try / finally字节代码,除非有Throwable,否则不执行该代码。

答案 1 :(得分:1)

我不相信这两种情况之间应该有significant的差异。根据java docsTry with resource翻译如下。

假设您尝试使用资源看起来像

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

将翻译为:

{
    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();
            }
        }
    }
}

很明显,尝试使用资源和普通try-catchnearly same。唯一的区别是您可以在翻译try with resource期间生成/不执行所有检查。

  

有人可以帮助解释为什么有时这么大的原因   延迟?

有时大延迟可能是由于当时的CPU使用率,可用内存,GC等原因造成的。