我正在寻找一个时间毫秒长的日期格式化程序的Java实现。我不想使用SimpleDateFormatter,因为它会为GC产生垃圾。我正在寻找一个快速,无垃圾的Java实现。有人在某处看过吗?
StringBuilder sb = getReusableStringBuilder();
parse(sb, System.currentTimeMillis());
编辑:这是一个日志库,因此它也必须包含时间。
答案 0 :(得分:3)
这是一个记录时间/日期的后台记录器和一个完全在后台记录的StringBuilder。每次呼叫的典型延迟低于1微秒。它可以回收所有物品,因此不会产生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在每次更改时(即每天一次)格式化日期,并使用除法生成时间和秒。
注意:通过创建一个String,即使你不使用棘手的StringBuilder,你仍然会产生垃圾(String和char [])。
我会附加一个回收的ByteBuffer以避免任何GC。 (午夜时分除外)
由于@Joachim Saucer建议格式化程序可用于减少垃圾产生。我怀疑除非你也放弃了String的制作,否则它不会产生太大的影响。
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
答案 1 :(得分:0)
为此,我使用DateTimes实用程序类创建了库。在内部,它采用自1970/01/01 00:00:00000之后的毫秒的“长值”并计算年,月,日,小时,分钟,秒和毫秒值。然后它将此信息作为ASCII字符串放入提供的字节数组中,没有用于GC的新对象。可以使用System.out.write()方法将此字节数组打印到控制台,而无需创建新的String对象。
您可以从我的网站here获取库作为jar文件。文章描述了用法并比较了性能。
答案 2 :(得分:-1)
解决方案比我想象的要简单得多:bootstrap FieldPosition所以它不会泄漏内存。
答案 3 :(得分:-3)
当您需要处理与日期和时间相关的任何事情时,请始终使用Joda Time。它还包含格式化程序。