背景

15年在中信银行做持续集成时,由于当时的项目是基于三方采购的 Java 配置开发平台做的,平台自己基于 Ant 插件实现了增量和热部署。

其中有几个项目在持续集成部署时,经常发现 Linux 平台部署成功后(Windows 不会出现,Linux 也是偶发现象),新版本代码并没有生效(反编译 class)。

起初我是在本地 windows 上跟踪调试基于 Ant 插件的代码,但始终重现不了(最后测试发现 Windows 无此 Bug)。

后来,通过分析代码逻辑,其中有段逻辑是通过文件的最后修改时间(File.lastModified())来判断要不要覆盖部署的,最后通过单测发现,是由于 JavaFile.lastModified() 方法在 WindowsLinux/Unix 平台获取的精度不一样导致的,Windows 精度为毫秒,而 Linux/Unix 只能到秒(JDK Bug:JDK-8177809)。

所以也解释了,为什么是偶发现象,文件修改时间如果判断的两个值正好跨秒时,部署就是成功的,否则失败。

Bug 重现

测试代码:FileTest.java

  1. import java.io.File;
  2. import java.io.IOException;
  3. import java.nio.file.Files;
  4. import java.text.SimpleDateFormat;
  5. public class FileTest {
  6. private static final long LM = 1599276952718L;
  7. public static void main(String[] args) throws IOException {
  8. // java版本号
  9. System.out.println("Java Version:" + System.getProperty("java.version"));
  10. File f = new File("test.txt");
  11. f.createNewFile();
  12. // 设置最后修改时间
  13. f.setLastModified(LM);
  14. // 获取修改时间,存在 bug
  15. System.out.printf("Test f.lastModified [%s]: %b\n",
  16. f.lastModified(), f.lastModified() == LM);
  17. // 格式化输出,正确不存在 bug
  18. System.out.printf("Test f.lastModified DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));
  19. // Files.getLastModifiedTime() 获取修改时间,同样存在 bug
  20. System.out.printf("Test Files.getLastModifiedTime [%s]: %b\n",
  21. Files.getLastModifiedTime(f.toPath()).toMillis(),
  22. (Files.getLastModifiedTime(f.toPath()).toMillis() == LM));
  23. // 格式化输出,正确不存在 bug
  24. System.out.printf("Test Files.getLastModifiedTime DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));
  25. f.delete();
  26. }
  27. }

命令行下编译、执行:

  1. # 编译执行
  2. $ javac FileTest.java && java FileTest

Windows 执行结果

Windows 平台不存在此 Bug。

  1. # 编译执行
  2. $ javac FileTest.java && java FileTest
  3. Java Version1.8.0_202
  4. Test f.lastModified [1599276952718]: true
  5. Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
  6. Test Files.getLastModifiedTime [1599276952718]: true
  7. Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

Mac 执行结果

JDK 8 最新版本,目前仍然没有修复该问题。

  1. # 编译执行
  2. $ javac FileTest.java && java FileTest
  3. Java Version1.8.0_261
  4. Test f.lastModified [1599276952000]: false
  5. Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
  6. Test Files.getLastModifiedTime [1599276952000]: false
  7. Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

Linux 执行结果

  1. # 编译执行
  2. $ javac FileTest.java && java FileTest
  3. Java Version1.8.0_171
  4. Test f.lastModified [1599276952000]: false
  5. Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
  6. Test Files.getLastModifiedTime [1599276952000]: false
  7. Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

官方 Bug 链接

JDK10 已修复,但是之前版本目前仍然未修复。

微信公众号:daodaotest

当年偶然发现的 Java Bug(JDK 9及之前仍未修复)的更多相关文章

  1. Java的cmd配置(也即Java的JDK配置及相关常用命令)——找不到或无法加载主类 的解决方法

    Java的cmd配置(也即Java的JDK配置及相关常用命令) ——找不到或无法加载主类  的解决方法 这段时间一直纠结于cmd下Java无法编译运行的问题.主要问题描述如下: javac 命令可以正 ...

  2. Java 序列化 JDK序列化总结

    Java 序列化 JDK序列化总结 @author ixenos Java序列化是在JDK 1.1中引入的,是Java内核的重要特性之一.Java序列化API允许我们将一个对象转换为流,并通过网络发送 ...

  3. Java中JDK和JRE的区别是什么?它们的作用分别是什么?

    JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,但是JRE是可以独立安装的,它们在Java开发和运行的时候起到不同的作用~ 1.JDK JDK是Java Development Kit ...

  4. Java多线程--JDK并发包(2)

    Java多线程--JDK并发包(2) 线程池 在使用线程池后,创建线程变成了从线程池里获得空闲线程,关闭线程变成了将线程归坏给线程池. JDK有一套Executor框架,大概包括Executor.Ex ...

  5. 【转】Java中JDK和JRE的区别是什么?它们的作用分别是什么?

    原文地址:http://blog.csdn.net/qq_33642117/article/details/52143824 JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,但是JRE ...

  6. centos7中配置java + mysql +jdk +使用jar部署项目

    centos7中配置java + mysql +jdk  +使用jar部署项目 思维导图 1. 配置JDK環境 1.1下载jdk安装包 Java Downloads | Oracle 1.2 将下载j ...

  7. How to Install JAVA 8 (JDK/JRE 8u111) on Debian 8 & 7 via PPA

    Oracle JAVA 8 Stable release has been released on Mar,18 2014 and available to download and install. ...

  8. 假设说这个世界不是真实存在的,仅仅是一段代码,迄今为止你发现了哪些bug?

    给这个世界写代码的不是一个人,而是一个团队(这么大的项目,一个人开发不了).并且严重怀疑这个一个开源项目.开发人员被我们觉得是神,所以一神论是不正确的,众神论才是真理,且凡人是有机会成为神的(參悟神道 ...

  9. Java之.jdk卸载-Linux

    Java之.jdk卸载-Linux 卸载Linux自带的jdk 首先查询: #  rpm -qa | grep jdk 使用root账户,进行卸载: # yum -y remove xxxxxxxx( ...

随机推荐

  1. DeepVO: Towards End-to-End Visual Odometry with Deep Recurrent Convolutional Neural Networks

    1.Introduction DL解决VO问题:End-to-End VO with RCNN 2.Network structure a.CNN based Feature Extraction 论 ...

  2. (转)交叉编译lrzsz

    交叉编译lrzsz 2016-03-20 1. 系统环境: Distributor ID:    Ubuntu Description:    Ubuntu 14.04.1 LTS Release:  ...

  3. 群晖系统如何通过Video Station套件管理NAS中的视频

    一.PC端观看视频 1.在NAS套件中心找到Video Station套件,安装套件 2.设置video套件别名,便于后期使用,控制面板----应用程序门户----video Station 3.选中 ...

  4. Vue中v-model指令的常用修饰符

    v-model指令有三个可以选用的修饰符:.lazy..number以及.trim.vue官方对此的描述为: .number-输入字符串转为有效的数字 .lazy-取代input监听change事件 ...

  5. 2020-07-16:如何获得一个链表的倒数第n个元素?

    福哥答案2020-07-16: 1.快慢指针.快指针先走n步,然后快慢指针同时走,直到快指针走到尾.2.两次遍历.第一次遍历获取链表长度,然后计算出序号,然后遍历获取序号下的元素.3.数组保存.遍历一 ...

  6. 对象原型之__proto__

    对象都会有一个__proto__指向构造函数的prototype原型对象,对象之所以能够使用构造函数的prototype原型对象的方法,就是因为有__proto__原型的存在.       funct ...

  7. DAPP开发初探——永存的留言

    转载地址 https://blog.csdn.net/qq_33764491/article/details/80570266 前言 最近DAPP的开发貌似很火,学习了区块链的一些知识之后,相信有很多 ...

  8. Linux下如何高效切换目录?

    Linux 下对于目录的切换,大家肯定会想到一个命令:cd 命令.这个是 Linux 下再基本不过的命令,如果这个命令都不知道的话,赶紧剖腹自尽去吧. cd 命令确实很方便,但如果需要频繁在下面的目录 ...

  9. 【java】java获取JVM启动参数 System.getProperty

    java获取JVM启动参数 System.getProperty取 -D后的key即可 public class Test { public static void main(String[] arg ...

  10. Java多线程_缓存对齐

    1.什么是缓存对齐 当前的电脑中,数据存储在磁盘上,可以断电保存,但是读取效率较低.不断电的情况下,数据可以在内存中存储,相对硬盘效率差不多是磁盘的一万倍左右.但是运算时,速度最快的是直接缓存在CPU ...