java - 格式化毫秒到日期字符串没有 SimpleDateFormatter(产生垃圾)

标签 java

我正在寻找纪元毫秒长的日期格式化程序的 Java 实现。我不想使用 SimpleDateFormatter,因为它会向 GC 产生垃圾。我正在寻找一种快速且无垃圾的 Java 实现。有人在哪里见过吗?

StringBuilder sb = getReusableStringBuilder();

parse(sb, System.currentTimeMillis());

编辑:这是一个日志库,所以它也必须包括时间。

最佳答案

这是一个记录时间/日期的后台记录器和一个完全在后台记录的 StringBuilder。每次调用的典型延迟低于一微秒。它回收一切,因此不会产生 GC。

这比使用队列在两个线程之间传递工作要高效得多。不幸的是,所有队列实现都会产生垃圾。 :(

import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class BackgroundLogger implements Runnable {
  static final int ENTRIES = 64;

  static class LogEntry {
    long time;
    int level;
    final StringBuilder text = new StringBuilder();
  }

  static class LogEntries {
    final LogEntry[] lines = new LogEntry[ENTRIES];
    int used = 0;
  }

  private final ExecutorService executor = Executors.newSingleThreadExecutor();
  final Exchanger<LogEntries> logEntriesExchanger = new Exchanger<LogEntries>();
  LogEntries entries = new LogEntries();

  BackgroundLogger() {
    executor.submit(this);
  }

  // put whatever you want in the StringBuilder, before the next call!
  public StringBuilder log(int level) {
    try {
      if (entries.used == ENTRIES)
        entries = logEntriesExchanger.exchange(entries);
      LogEntry le = entries.lines[entries.used++];
      le.time = System.currentTimeMillis();
      le.level = level;
      return le.text;

    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }

  public void flush() throws InterruptedException {
    entries = logEntriesExchanger.exchange(entries);
    entries = logEntriesExchanger.exchange(entries);
  }

  public void stop() {
    try {
      flush();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    executor.shutdownNow();
  }

  @Override
  public void run() {
    LogEntries entries = new LogEntries();
    try {
      while(!Thread.interrupted()) {
        entries = logEntriesExchanger.exchange(entries);
        for (int i = 0; i < entries.used; i++) {
          bgLog(entries.lines[i]);
          entries.lines[i].text.delete(0, entries.lines[i].text.length());
        }
        entries.used = 0;
      }
    } catch (InterruptedException ignored) {

    } finally {
      System.out.println("logger stopping.");
    }
  }

  private void bgLog(LogEntry line) {
    // log the entry to a file.
  }
}

我写了一个。

如果你允许一点点垃圾,你可以简化你的问题。您可以使用 SimpleDateFormatter 在每次更改时格式化日期(即每天一次)并使用除法生成时间和秒数。

注意:通过创建字符串,您仍然会产生垃圾(一个字符串和一个 char[],即使您不使用棘手的 StringBuilder)。

我会附加到回收的 ByteBuffer 以避免任何 GC。 (午夜时分除外)


正如@Joachim Saucer 所建议的那样,格式化程序可用于产生更少的垃圾。我怀疑除非您也放弃字符串的生成,否则不会有太大的不同。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
StringBuffer sb = new StringBuffer();
Date tmpDate = new Date();
final FieldPosition pos = new FieldPosition(0);
{
  long free1 = Runtime.getRuntime().freeMemory();
  for (int i = 0; i < 1000; i++) {
    tmpDate.setTime(System.currentTimeMillis());
    sdf.format(tmpDate, sb, pos);
    sb.delete(0, sb.length());
  }
  long free2 = Runtime.getRuntime().freeMemory();
  if (free1 == free2) throw new Error("This must be run with -XX:-UseTLAB");
  System.out.println("SDF.format used an average of " + (free1 - free2) / 1000 + " bytes");
}
{
  long free1 = Runtime.getRuntime().freeMemory();
  for (int i = 0; i < 1000; i++) {
    tmpDate.setTime(System.currentTimeMillis());
    sdf.format(tmpDate, sb, pos);
    String str = sb.toString();
    sb.delete(0, sb.length());
  }
  long free2 = Runtime.getRuntime().freeMemory();
  if (free1 == free2) throw new Error("This must be run with -XX:-UseTLAB");
  System.out.println("SDF.format with a String used an average of " + (free1 - free2) / 1000 + " bytes");
}

打印

SDF.format used an average of 24 bytes
SDF.format with a String used an average of 120 bytes

关于java - 格式化毫秒到日期字符串没有 SimpleDateFormatter(产生垃圾),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7389104/

相关文章:

java - 在 java 中使用 Bouncy CaSTLe 进行 block 式 RSA 加密

java - 在 RabbitMQ 中删除或更新消息

java - 另一个 java.lang.ClassCastException

java - 运行时使用的代码是否有自动突出显示

java - 为 checkin checkout 系统保存数据的简单方法

java - 防止表单重新提交并在页面刷新时插入数据库

java - 在 Spring Data JPA 字段中使用投影错误

java - 发现了多个名为 [spring_web] 的片段。这对于相对顺序来说是不合法的

java - 如何使用 Inno Setup 制作安装程序?

java - 正在运行的应用程序上的 Swing JLabel 文本更改