Java 不使用SimpleDataFormatter(产生垃圾)格式化毫秒到日期字符串
我正在寻找一个日期格式化程序的Java实现,其长度为毫秒。我不想使用SimpleDataFormatter,因为它会向GC产生垃圾。我正在寻找一个快速且无垃圾的Java实现。有人在什么地方见过吗Java 不使用SimpleDataFormatter(产生垃圾)格式化毫秒到日期字符串,java,Java,我正在寻找一个日期格式化程序的Java实现,其长度为毫秒。我不想使用SimpleDataFormatter,因为它会向GC产生垃圾。我正在寻找一个快速且无垃圾的Java实现。有人在什么地方见过吗 StringBuilder sb = getReusableStringBuilder(); parse(sb, System.currentTimeMillis()); 编辑:这是一个日志库,所以它必须包括时间。这里有一个后台记录器,记录时间/日期,还有一个StringBuilder,完全在后台记
StringBuilder sb = getReusableStringBuilder();
parse(sb, System.currentTimeMillis());
编辑:这是一个日志库,所以它必须包括时间。这里有一个后台记录器,记录时间/日期,还有一个StringBuilder,完全在后台记录。每次呼叫的典型延迟低于一微秒。它回收所有东西,因此不会产生GC 这比使用队列在两个线程之间传递工作要高效得多。所有队列实现都会创建垃圾:(
当您需要处理任何与日期和时间相关的内容时,请始终使用。它还包含格式化程序。解决方案比我想象的要简单得多:引导FieldPosition,这样它就不会泄漏内存。我使用DateTimes实用程序类创建了库,用于此目的。它在内部采用“长值”自1970/01/01 00:00:00.000以来的毫秒数,并计算年、月、日、小时、分钟、秒和毫秒值。然后将此信息作为ASCII字符串放入提供的字节数组中,GC没有新对象。此字节数组可以使用System.out.write()方法打印到控制台,而无需创建新的字符串对象
您可以从我的网站上以jar文件的形式获取该库。文章描述了使用情况并比较了性能。如果您不想产生垃圾,请不要使用垃圾收集语言。为什么您会关心SimpleDataFormat产生的垃圾?如果避免垃圾是您的目标,那么为什么您会变得完全有效
long
不可处理地有效(可能可以避免)首先是String
对象?您是否检查了SimpleDataFormat
产生的垃圾量?特别是如果您使用的话?因为我希望垃圾量很小。@Peter Lawrey:这是我的观点。这看起来像是过早优化。要么日志级别设置为跟踪,要么日志代码会经常执行,but调用代码将生成比日志库本身多得多的垃圾(例如,必须记录的所有字符串),或者它被设置为错误,生成一些垃圾不会造成任何伤害,因为它不会经常被调用。@Raedwald:我永远不会这样做。您可以从StringBuilder中获取内容,而不创建字符串。也许您可以解释如何使用它,使它生成最小GC。我会使用StringBuilder不生成字符串,因此格式化逻辑c会写信给StringBuilder。我正在改变我的问题以澄清这一点。你的方法很酷,Peter。我不介意缓存日期并只计算每个日志上的时间。但是时间逻辑可能会很痛苦,特别是白天节省时间、时区等。在开始自己编写代码之前,我想花点时间看看是否可以完成重复使用FieldPosition
将平均内存使用量减少到24个字节(再少32个字节!)。这可能是节省,因为您已经共享了SimpleDataFormat
和StringBuffer
,所以无论如何它都需要以某种方式进行同步。@Peter:测量内存使用量的非常酷的方法,我不知道UseTLAB
。我真的想不止一次地+1这个答案。@Sergio Olivera Jr.,您可以使用ThreadLocal
用于日志记录。由于没有人指导我使用实现格式化逻辑的代码,因此如果没有其他问题,我将选择此答案。
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.
}
}
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