一、簡(jiǎn)介
Epsilon(A No-Op Garbage Collector)垃圾回收器控制內(nèi)存分配橘忱,但是不執(zhí)行任何垃圾回收工作顷扩。一旦java的堆被耗盡火邓,jvm就直接關(guān)閉。設(shè)計(jì)的目的是提供一個(gè)完全消極的GC實(shí)現(xiàn)烂叔,分配有限的內(nèi)存分配谨胞,最大限度降低消費(fèi)內(nèi)存占用量和內(nèi)存吞吐時(shí)的延遲時(shí)間。一個(gè)好的實(shí)現(xiàn)是隔離代碼變化长已,不影響其他GC畜眨,最小限度的改變其他的JVM代碼。
二术瓮、使用場(chǎng)景
- Performance testing,什么都不執(zhí)行的GC非常適合用于差異性分析。no-op GC可以用于過濾掉GC誘發(fā)的新能損耗贰健,比如GC線程的調(diào)度胞四,GC屏障的消耗,GC周期的不合適觸發(fā)伶椿,內(nèi)存位置變化等辜伟。此外有些延遲者不是由于GC引起的,比如scheduling hiccups, compiler transition hiccups脊另,所以去除GC引發(fā)的延遲有助于統(tǒng)計(jì)這些延遲导狡。
- Memory pressure testing, 在測(cè)試java代碼時(shí),確定分配內(nèi)存的閾值有助于設(shè)置內(nèi)存壓力常量值偎痛。這時(shí)no-op就很有用旱捧,它可以簡(jiǎn)單地接受一個(gè)分配的內(nèi)存分配上限,當(dāng)內(nèi)存超限時(shí)就失敗踩麦。例如:測(cè)試需要分配小于1G的內(nèi)存枚赡,就使用-Xmx1g參數(shù)來配置no-op GC,然后當(dāng)內(nèi)存耗盡的時(shí)候就直接crash谓谦。
- VM interface testing, 以VM開發(fā)視角贫橙,有一個(gè)簡(jiǎn)單的GC實(shí)現(xiàn),有助于理解VM-GC的最小接口實(shí)現(xiàn)反粥。它也用于證明VM-GC接口的健全性卢肃。
- Extremely short lived jobs, 一個(gè)短聲明周期的工作可能會(huì)依賴快速退出來釋放資源,這個(gè)時(shí)候接收GC周期來清理heap其實(shí)是在浪費(fèi)時(shí)間才顿,因?yàn)閔eap會(huì)在退出時(shí)清理莫湘。并且GC周期可能會(huì)占用一會(huì)時(shí)間,因?yàn)樗蕾噃eap上的數(shù)據(jù)量娜膘。
- Last-drop latency improvements, 對(duì)那些極端延遲敏感的應(yīng)用逊脯,開發(fā)者十分清楚內(nèi)存占用,或者是幾乎沒有垃圾回收的應(yīng)用竣贪,此時(shí)耗時(shí)較長(zhǎng)的GC周期將會(huì)是一件壞事军洼。
- Last-drop throughput improvements, 即便對(duì)那些無需內(nèi)存分配的工作巩螃,選擇一個(gè)GC意味著選擇了一系列的GC屏障,所有的OpenJDK GC都是分代的匕争,所以他們至少會(huì)有一個(gè)寫屏障避乏。避免這些屏障可以帶來一點(diǎn)點(diǎn)的吞吐量提升。
三甘桑、案例
使用G1垃圾收集器
代碼:
public class TestEpsilon {
public static void main(String[] args) {
System.out.println("程序開始");
boolean flag = true;
List<Garbage> list = new ArrayList<>();
long count = 0;
while (flag) {
list.add(new Garbage(list.size() + 1));
if (list.size() == 1000000 && count == 0) {
list.clear();
count++;
}
}
System.out.println("程序結(jié)束");
}
}
class Garbage {
private int number;
public Garbage(int number) {
this.number = number;
}
/**
* GC在清除對(duì)象時(shí)拍皮,會(huì)調(diào)用finalize()方法
*/
@Override
public void finalize() {
System.out.println(this + " : " + number + " is dying");
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
啟動(dòng)參數(shù):
-Xms100m -Xmx100m
運(yùn)行程序后,結(jié)果如下:
程序開始
...
com.gf.demo8.Garbage@15ddf76b : 305097 is dying
com.gf.demo8.Garbage@35e52705 : 305224 is dying
com.gf.demo8.Garbage@32c14bc1 : 305362 is dying
com.gf.demo8.Garbage@7521660a : 305705 is dying
com.gf.demo8.Garbage@f3da16a : 305948 is dying
com.gf.demo8.Garbage@13fc7287 : 306089 is dying
at java.base/java.lang.ref.Finalizer.register(Finalizer.java:66)
at java.base/java.lang.Object.<init>(Object.java:50)
at com.gf.demo8.Garbage.<init>(TestEpsilon.java:28)
at com.gf.demo8.TestEpsilon.main(TestEpsilon.java:14)
...
會(huì)發(fā)現(xiàn)G1一直回收對(duì)象跑杭,直到內(nèi)存不夠用铆帽。
使用Epsilon垃圾收集器
啟動(dòng)參數(shù):
UnlockExperimentalVMOptions:解鎖隱藏的虛擬機(jī)參數(shù)。
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms100m -Xmx100m
運(yùn)行程序后德谅,結(jié)果如下:
程序開始
Terminating due to java.lang.OutOfMemoryError: Java heap space
會(huì)發(fā)現(xiàn)很快就內(nèi)存溢出了爹橱,因?yàn)镋psilon不會(huì)去回收對(duì)象。