ここには、バックグラウンドでログするための時間/日付とStringBuilderを記録する背景ロガーがあります。典型的な待ち時間は1コールあたり1マイクロ秒未満です。 GCは何も生産されないようにすべてをリサイクルします。
これは、キューを使用して2つのスレッド間で作業を渡すよりもはるかに効率的です。残念ながら、すべてのキュー実装ではガベージが作成されます。 :(
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.
}
}
私が1を書かれている。
あなたがゴミの少しを許可する場合は、あなたの問題を簡素化することができます。あなたは、日付にそれが(つまり、一度変更するたびにフォーマットするSimpleDateFormatterを使用することができます
注:文字列を作成することによって、ごみ(StringBuilderを使用しない場合でも、文字列とchar [])が生成されます。
GCを避けるために、リサイクルされたByteBufferに追加します。 (夜中頃を除く)
@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
ガベージを生成したくない場合は、ガベージコレクション言語を使用しないでください。なぜあなたはSimpleDateFormatによって生成されたゴミを気にしていますか? –
@JB Nizet:私はこのオプションを持っていません。 Javaを使用する必要があり、GCの待ち時間を最小限に抑える必要があります。これは、低レイテンシの業界にとって珍しい要件ではありません。 – TraderJoeChicago
@JB Nizet、あなたのアプリケーションの1%未満がGCレスである必要がある場合、この要件に合うように他の99%を変更しますか? –