You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中不使用java.util.Date获取低GC高精度纳秒级时间

哥们,这个问题我做低延迟系统的时候踩过坑,太懂既要精准时间又要压GC的痛苦了!先给你掰扯清楚Date.getTime()的底层逻辑,再给你几个能落地的方案。

一、java.util.Date到底是怎么获取时间的?

其实你可能没注意到,Date的无参构造器本质上就是套了一层System.currentTimeMillis():

// OpenJDK里Date的无参构造源码
public Date() {
    this(System.currentTimeMillis());
}

而Date.getTime()只是返回它内部存的那个long型时间戳——就是构造时从System.currentTimeMillis()拿到的毫秒数。所以Date.getTime()的精度和System.currentTimeMillis()完全一致,都是毫秒级,依赖于操作系统的系统时钟。

为啥你觉得System.currentTimeMillis()精度不够?可能是在某些旧系统(比如老版本Windows)上,它的时钟粒度是10ms甚至更粗,但现在主流的Linux、macOS、新版Windows都已经做到了1ms级的精度,完全能满足大部分高精度时间需求。

至于System.nanoTime(),它确实是纳秒级精度,但它是单调递增的相对时间,不能用来表示真实的墙上时间(比如你要的准确年月日时分秒),而且起点不是epoch,所以确实不符合你的需求。

二、解决GC问题:别每次都新建Date对象

既然Date的核心就是存个long时间戳,那我们完全可以绕开频繁创建对象的坑,用这几个方案:

方案1:复用单个Date实例

如果你非得用Date类,可以维护一个全局的Date实例,每次获取时间时更新它的时间再取值:

private static final Date REUSABLE_DATE = new Date();

public static long getCurrentTimeMillisNoGC() {
    REUSABLE_DATE.setTime(System.currentTimeMillis());
    return REUSABLE_DATE.getTime();
}

这样整个程序生命周期只创建一次Date对象,彻底避免了因Date导致的GC。不过要注意线程安全:多线程环境下要加锁,或者用ThreadLocal给每个线程分配一个实例:

private static final ThreadLocal<Date> THREAD_LOCAL_DATE = ThreadLocal.withInitial(Date::new);

public static long getCurrentTimeMillisThreadSafe() {
    Date date = THREAD_LOCAL_DATE.get();
    date.setTime(System.currentTimeMillis());
    return date.getTime();
}

每个线程只创建一次Date,既安全又无GC压力。

方案2:直接用System.currentTimeMillis()

如果System.currentTimeMillis()的毫秒级精度能满足你,那直接用它就完事儿了——静态方法,不用创建任何对象,完全没GC开销,而且和Date.getTime()的底层实现一模一样,时间精度完全相同。

方案3:更高精度的墙上时间(如果需要纳秒级)

要是你需要比毫秒更细的真实时间,可以用Java 8+的java.time.Instant类,它能提供纳秒级精度(实际精度还是看操作系统的时钟能力)。Instant.now()底层会调用更精准的系统API(比如Linux的clock_gettime()),获取方式如下:

public static long getCurrentNanos() {
    Instant instant = Instant.now();
    return instant.getEpochSecond() * 1_000_000_000 + instant.getNano();
}

虽然每次调用会创建一个Instant对象,但它是轻量级的,GC压力比Date小很多。要是想彻底避免GC,那只能用Unsafe直接调用系统时钟API,但这属于黑科技,不推荐在生产环境用。

三、关于每天仅触发1次GC的目标

要实现这个目标,除了时间获取这块要减少对象创建,还要注意其他业务代码的内存开销,比如避免频繁创建小对象、合理设置JVM参数等。但针对时间获取的场景,上面的方案已经能完全消除这部分的GC触发源了。

内容的提问来源于stack exchange,提问作者SomeGuy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:01:16