เป็นไปได้ไหมที่จะบังคับให้มีการรวบรวมขยะใน Java แม้ว่ามันจะยุ่งยากหรือไม่ ฉันรู้System.gc();
และRuntime.gc();
พวกเขาแนะนำให้ทำ GC เท่านั้น ฉันจะบังคับ GC ได้อย่างไร
เป็นไปได้ไหมที่จะบังคับให้มีการรวบรวมขยะใน Java แม้ว่ามันจะยุ่งยากหรือไม่ ฉันรู้System.gc();
และRuntime.gc();
พวกเขาแนะนำให้ทำ GC เท่านั้น ฉันจะบังคับ GC ได้อย่างไร
คำตอบ:
ตัวเลือกที่ดีที่สุดของคุณคือการโทรหาSystem.gc()
ซึ่งเป็นเพียงคำใบ้สำหรับตัวรวบรวมขยะที่คุณต้องการให้ทำเพื่อรวบรวม ไม่มีทางที่จะบังคับและเก็บอย่างฉับพลันแม้ว่าตัวเก็บรวบรวมขยะจะไม่สามารถกำหนดค่าได้
non-deterministic == trouble
GC.Collect()
ไม่ได้เก็บรวบรวม ในชวาgc()
ไม่
ห้องสมุด jlibs มีระดับยูทิลิตี้ที่ดีสำหรับการเก็บขยะ คุณสามารถบังคับให้การรวบรวมขยะโดยใช้เคล็ดลับเล็ก ๆ น้อย ๆ กับวัตถุWeakReference
RuntimeUtil.gc ()จาก jlibs:
/**
* This method guarantees that garbage collection is
* done unlike <code>{@link System#gc()}</code>
*/
public static void gc() {
Object obj = new Object();
WeakReference ref = new WeakReference<Object>(obj);
obj = null;
while(ref.get() != null) {
System.gc();
}
}
PhantomReference
ด้วยReferenceQueue
และจากนั้นคุณจะได้รับแจ้งหลังจากการสรุป แต่ยังคงทำความสะอาดก่อน ในที่สุดแม้ว่าคุณจะตรวจพบสำเร็จว่าหน่วยความจำสำหรับวัตถุนี้ถูกเรียกคืนก็ยังคงมีความหมายน้อยมากใน GC generational เช่น HotSpot โดยปกติมันจะตรงกับการทำความสะอาดของคนรุ่นใหม่
System.gc(); System.gc();
มัน แต่มันน่าสนใจที่จะรู้ว่ามันใช้งานได้ดีกว่านั้นหรือไม่ ในความเป็นจริงเพียงพิมพ์จำนวนครั้งที่เรียกว่าSystem.gc()
จะเพียงพอ โอกาสที่จะได้ถึง 2 นั้นค่อนข้างบาง
วิธีที่ดีที่สุด (ถ้าไม่เพียง แต่) เพื่อบังคับให้ GC ต้องเขียน JVM ที่กำหนดเอง ฉันเชื่อว่านักสะสมขยะสามารถเสียบปลั๊กได้ดังนั้นคุณอาจจะเลือกใช้หนึ่งในนั้นและปรับแต่งมัน
หมายเหตุ: นี่ไม่ใช่คำตอบที่ง่าย
ใช้ฟังก์ชันJava ™ Virtual Machine Tool Interface (JVM TI) ,
jvmtiError ForceGarbageCollection(jvmtiEnv* env)
จะ "บังคับให้ VM เพื่อทำการรวบรวมขยะ" JVM ที่ TI เป็นส่วนหนึ่งของJavaTM แพลตฟอร์มดีบักเกอร์สถาปัตยกรรม (JPDA)
ใช่เกือบจะเป็นไปได้ที่จะบังคับให้คุณต้องโทรหาวิธีการในลำดับเดียวกันและในเวลาเดียวกันสิ่งนี้คือ:
System.gc ();
System.runFinalization ();
แม้ว่าจะเป็นเพียงวัตถุเดียวในการล้างการใช้สองวิธีนี้ในเวลาเดียวกันบังคับให้ตัวรวบรวมขยะใช้finalise()
วิธีการของวัตถุที่ไม่สามารถเข้าถึงได้ซึ่งทำให้หน่วยความจำที่กำหนดไม่สามารถเข้าถึงได้และทำสิ่งที่finalize()
วิธีนั้นระบุไว้
อย่างไรก็ตามมันเป็นวิธีปฏิบัติที่แย่มากที่จะใช้ตัวรวบรวมขยะเพราะการใช้มันสามารถแนะนำการโหลดเกินพิกัดให้กับซอฟต์แวร์ที่อาจเลวร้ายยิ่งกว่าในหน่วยความจำตัวเก็บขยะมีเธรดของตัวเองซึ่งไม่สามารถควบคุมได้และขึ้นอยู่กับ อัลกอริทึมที่ใช้โดย gc อาจใช้เวลามากขึ้นและพิจารณาว่าไม่มีประสิทธิภาพมากคุณควรตรวจสอบซอฟต์แวร์ของคุณถ้ามันแย่ที่สุดด้วยความช่วยเหลือของ gc เพราะมันพังแน่นอนทางออกที่ดีจะต้องไม่ขึ้นอยู่กับ gc
หมายเหตุ:โปรดจำไว้ว่าสิ่งนี้จะใช้ได้เฉพาะในวิธีการสุดท้ายไม่ใช่การกำหนดใหม่ของวัตถุหากสิ่งนี้เกิดขึ้นวัตถุจะยังมีชีวิตอยู่และจะมีการฟื้นคืนชีพซึ่งเป็นไปได้ทางเทคนิค
gc()
เป็นเพียงคำแนะนำในการเรียกใช้การรวบรวมขยะ runFinalizers()
รัน finalizers บนวัตถุเท่านั้น "ซึ่งถูกค้นพบว่าถูกทิ้ง" หากประชาคมโลกไม่ได้ทำงานจริงอาจจะไม่มีวัตถุดังกล่าว ...
ภายใต้เอกสารประกอบสำหรับOutOfMemoryErrorมันจะประกาศว่าจะไม่ถูกส่งออกไปเว้นแต่ว่า VM จะไม่สามารถเรียกคืนหน่วยความจำหลังจากการรวบรวมขยะเต็ม ดังนั้นหากคุณจัดสรรหน่วยความจำต่อไปจนกว่าคุณจะได้รับข้อผิดพลาดคุณจะต้องบังคับให้มีการรวบรวมขยะเต็ม
คำถามที่คุณอยากถามจริงๆก็คือ "ฉันจะเรียกคืนหน่วยความจำที่ฉันคิดว่าฉันควรจะเรียกคืนได้ด้วยการรวบรวมขยะ"
หากต้องการร้องขอ GC (ไม่ใช่จาก System.gc ()) ด้วยตนเอง:
.gc เป็นผู้สมัครเพื่อขจัดการปล่อยรุ่นต่อ ๆ ไป - วิศวกรของซันเคยแสดงความคิดเห็นว่าอาจมีคนในโลกน้อยกว่ายี่สิบคนที่รู้วิธีใช้. gc () - ฉันทำงานคืนหนึ่งเมื่อสองสามชั่วโมงที่ศูนย์ / สำคัญ โครงสร้างข้อมูลโดยใช้ข้อมูลที่สร้างโดย SecureRandom ที่บางที่ผ่านมา 40,000 วัตถุ vm จะช้าลงราวกับว่ามันหมดพอยน์เตอร์ เห็นได้ชัดว่ามันสำลักลงบนตารางตัวชี้ 16 บิตและแสดงพฤติกรรม "เครื่องจักรที่ล้มเหลว" แบบคลาสสิก
ฉันพยายาม -Xms และต่อ ๆ ไปเรื่อย ๆ นิด ๆ หน่อย ๆ จนกว่ามันจะวิ่งไปประมาณ 57, xxx อะไรบางอย่าง จากนั้นมันจะเรียกใช้ gc จาก 57,127 ไปเป็น 57,128 หลังจาก gc () - ที่ระดับความเร็วของโค้ดที่ค่าย Easy Money
การออกแบบของคุณต้องการการทำงานขั้นพื้นฐานอาจเป็นวิธีการเลื่อนหน้าต่าง
คุณสามารถทริกเกอร์ GC จากบรรทัดคำสั่ง สิ่งนี้มีประโยชน์สำหรับชุด / crontab:
jdk1.7.0/bin/jcmd <pid> GC.run
ดู:
ข้อกำหนด JVM ไม่ได้พูดอะไรที่เฉพาะเจาะจงเกี่ยวกับการรวบรวมขยะ ด้วยเหตุนี้ผู้ขายจึงสามารถใช้งาน GC ได้อย่างอิสระ
ดังนั้นความไม่ชัดเจนนี้ทำให้เกิดความไม่แน่นอนในพฤติกรรมการเก็บขยะ คุณควรตรวจสอบรายละเอียด JVM ของคุณเพื่อทราบเกี่ยวกับแนวทาง / อัลกอริทึมการรวบรวมขยะ นอกจากนี้ยังมีตัวเลือกในการปรับแต่งพฤติกรรมเช่นกัน
หากคุณต้องการบังคับให้มีการรวบรวมขยะบางทีคุณควรพิจารณาว่าคุณจัดการทรัพยากรอย่างไร คุณกำลังสร้างวัตถุขนาดใหญ่ที่ยังคงอยู่ในหน่วยความจำหรือไม่? คุณกำลังสร้างวัตถุขนาดใหญ่ (เช่นคลาสกราฟิก) ที่มีDisposable
อินเทอร์เฟซและไม่โทรdispose()
เมื่อทำเสร็จหรือไม่ คุณกำลังประกาศบางสิ่งบางอย่างในระดับชั้นเรียนที่คุณต้องการเพียงวิธีเดียวหรือไม่?
มันจะดีกว่าถ้าคุณจะอธิบายเหตุผลที่คุณต้องการเก็บขยะ หากคุณใช้ SWT คุณสามารถกำจัดทรัพยากรเช่นImage
และFont
เพื่อเพิ่มหน่วยความจำ ตัวอย่างเช่น
Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();
นอกจากนี้ยังมีเครื่องมือในการกำหนดทรัพยากรที่ไม่ถูกเปิดเผย
หากคุณกำลังวิ่งออกมาจากหน่วยความจำและได้รับOutOfMemoryException
คุณสามารถลองเพิ่มจำนวนของพื้นที่กองใช้ได้กับจาวาโดยเริ่มต้นที่คุณมีโปรแกรมแทนเพียงjava -Xms128m -Xmx512m
java
สิ่งนี้จะทำให้คุณมีขนาดฮีพเริ่มต้นที่ 128Mb และสูงสุด 512Mb ซึ่งไกลเกินกว่ามาตรฐาน 32Mb / 128Mb
java -Xms512M -Xmx1024M
ตัวเลือกอื่นคือการไม่สร้างวัตถุใหม่
การรวมออบเจกต์ไม่อยู่เพื่อลดความต้องการ GC ใน Java
การรวมวัตถุโดยทั่วไปจะไม่เร็วกว่าการสร้างวัตถุ (โดยเฉพาะสำหรับวัตถุที่มีน้ำหนักเบา) แต่จะเร็วกว่าการรวบรวมขยะ หากคุณสร้าง 10,000 วัตถุและแต่ละวัตถุคือ 16 ไบต์ นั่นคือ 160,000 ไบต์ GC ต้องเรียกคืน ในทางกลับกันหากคุณไม่ต้องการ 10,000 ทั้งหมดในเวลาเดียวกันคุณสามารถสร้างพูลเพื่อรีไซเคิล / นำวัตถุที่ไม่จำเป็นต้องสร้างวัตถุใหม่มาใช้ใหม่และกำจัดความต้องการของวัตถุเก่า GC
บางสิ่งเช่นนี้ (ยังไม่ทดลอง) และถ้าคุณต้องการให้มันปลอดภัยต่อเธรดคุณสามารถสลับ LinkedList สำหรับ ConcurrentLinkedQueue
public abstract class Pool<T> {
private int mApproximateSize;
private LinkedList<T> mPool = new LinkedList<>();
public Pool(int approximateSize) {
mApproximateSize = approximateSize;
}
public T attain() {
T item = mPool.poll();
if (item == null) {
item = newInstance();
}
return item;
}
public void release(T item) {
int approxSize = mPool.size(); // not guaranteed accurate
if (approxSize < mApproximateSize) {
recycle(item);
mPool.add(item);
} else if (approxSize > mApproximateSize) {
decommission(mPool.poll());
}
}
public abstract T newInstance();
public abstract void recycle(T item);
public void decommission(T item) { }
}
บน OracleJDK 10 ที่มี G1 GC การเรียกเพียงครั้งเดียวที่System.gc()
จะทำให้ GC ทำความสะอาดคอลเล็กชันเก่า ฉันไม่แน่ใจว่า GC ทำงานได้ทันทีหรือไม่ อย่างไรก็ตาม GC จะไม่ล้างคอลเลกชัน Young แม้ว่าSystem.gc()
จะมีการเรียกหลายครั้งในการวนซ้ำ ที่จะได้รับ GC ในการทำความสะอาดเก็บหนุ่มคุณต้องจัดสรรในวง (เช่นnew byte[1024]
) System.gc()
โดยไม่ต้องโทร โทรSystem.gc()
เหตุผลบางอย่างที่จะช่วยป้องกันการ GC จากการทำความสะอาดเก็บหนุ่ม
จริง ๆ ฉันไม่ได้รับคุณ แต่เพื่อให้ชัดเจนเกี่ยวกับ "การสร้างวัตถุที่ไม่มีที่สิ้นสุด" ฉันหมายความว่ามีบางส่วนของรหัสที่ระบบขนาดใหญ่ของฉันทำการสร้างวัตถุที่จัดการและมีชีวิตอยู่ในความทรงจำฉันไม่สามารถรับรหัสชิ้นนี้จริง ๆ เพียงแค่ท่าทาง !!
ถูกต้องท่าทางเท่านั้น คุณมีคำตอบทั่วไปที่ผู้โพสต์หลายคนได้รับ เอาอันนี้ทีละอัน:
ถูกต้องไม่มี jvm จริง - เป็นเพียงข้อมูลจำเพาะกลุ่มวิทยาการคอมพิวเตอร์ที่อธิบายพฤติกรรมที่ต้องการ ... ฉันเพิ่งขุดลงในการเริ่มต้นวัตถุ Java จากรหัสดั้งเดิม เพื่อให้ได้สิ่งที่คุณต้องการวิธีเดียวคือการทำสิ่งที่เรียกว่าก้าวร้าวโมฆะ ความผิดพลาดถ้าทำผิดนั้นแย่มากที่เราต้อง จำกัด ขอบเขตของคำถาม:
ผู้โพสต์ส่วนใหญ่ที่นี่จะถือว่าคุณกำลังพูดว่าคุณกำลังทำงานกับอินเทอร์เฟซถ้าเราจะต้องดูว่าคุณถูกส่งวัตถุทั้งหมดหรือรายการเดียวในเวลา
หากคุณไม่ต้องการวัตถุอีกต่อไปคุณสามารถกำหนด null ให้กับวัตถุได้ แต่หากคุณเข้าใจผิดจะมีข้อยกเว้นตัวชี้ null ที่สร้างขึ้น ฉันเดิมพันว่าคุณสามารถทำงานได้ดีขึ้นถ้าคุณใช้ NIO
เมื่อใดก็ตามที่คุณหรือฉันหรือใครก็ตามได้รับ: "ได้โปรดฉันต้องการสิ่งนั้นอย่างน่ากลัว " มันเป็นสิ่งที่เป็นสากลเกือบจะเป็นจุดเริ่มต้นของการทำลายล้างสิ่งที่คุณพยายามจะทำ รหัสจริงที่ใช้และแสดงคำถามของคุณให้เราทราบ
อย่าท้อแท้ บ่อยครั้งที่สิ่งนี้แก้ไขได้คือ DBA ของคุณใช้แพ็คเกจที่ซื้อที่ไหนสักแห่งและการออกแบบดั้งเดิมไม่ได้ถูกปรับแต่งสำหรับโครงสร้างข้อมูลขนาดใหญ่
นั่นเป็นเรื่องธรรมดามาก
FYI
วิธีการเรียก System.runFinalizersOnExit (จริง) รับประกันว่าวิธีการ finalizer จะเรียกว่าก่อนที่จะปิดตัวลง Java อย่างไรก็ตามวิธีนี้ไม่ปลอดภัยโดยเนื้อแท้และได้รับการคัดค้าน อีกทางเลือกหนึ่งคือการเพิ่ม“ hooks ปิด” กับวิธีการ Runtime.addShutdownHook
Masarrat Siddiqui
มีวิธีอ้อมในการบังคับให้เก็บขยะ คุณเพียงแค่ต้องเติม heap ด้วยวัตถุชั่วคราวจนกว่าจะถึงเวลาที่ตัวรวบรวมขยะจะทำงาน ฉันได้สร้างคลาสที่บังคับให้นักสะสมขยะด้วยวิธีนี้:
class GarbageCollectorManager {
private static boolean collectionWasForced;
private static int refCounter = 0;
public GarbageCollectorManager() {
refCounter++;
}
@Override
protected void finalize() {
try {
collectionWasForced = true;
refCounter--;
super.finalize();
} catch (Throwable ex) {
Logger.getLogger(GarbageCollectorManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
public int forceGarbageCollection() {
final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;
int iterationsUntilCollected = 0;
collectionWasForced = false;
if (refCounter < 2)
new GarbageCollectorManager();
while (!collectionWasForced) {
iterationsUntilCollected++;
int[] arr = new int[TEMPORARY_ARRAY_SIZE_FOR_GC];
arr = null;
}
return iterationsUntilCollected;
}
}
การใช้งาน:
GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();
ฉันไม่ทราบว่าวิธีการนี้มีประโยชน์มากเพียงใดเพราะมันจะเติมฮีปอย่างต่อเนื่อง แต่ถ้าคุณมีแอปพลิเคชั่นที่สำคัญซึ่งต้องบังคับ GC - เมื่อนี่เป็นวิธีพกพา Java เพื่อบังคับ GC
ฉันต้องการเพิ่มบางสิ่งที่นี่ โปรดอย่าใช้ Java ที่ทำงานบนเครื่องเสมือนและไม่ใช่เครื่องจริง เครื่องเสมือนมีวิธีการสื่อสารกับเครื่องของตัวเอง มันอาจหลากหลายจากระบบไปยังระบบ ตอนนี้เมื่อเราเรียก GC เราขอให้ Virtual Machine of Java เรียก Garbage Collector
เนื่องจาก Garbage Collector อยู่กับ Virtual Machine เราจึงไม่สามารถบังคับให้ทำการล้างข้อมูลที่นั่นแล้ว ค่อนข้างว่าเราจะจัดคิวคำขอของเรากับ Garbage Collector ขึ้นอยู่กับเครื่องเสมือนหลังจากเวลาใดเวลาหนึ่ง (อาจเปลี่ยนจากระบบหนึ่งไปอีกระบบหนึ่งโดยทั่วไปเมื่อหน่วยความจำ threshold ที่จัดสรรให้กับ JVM เต็ม) เครื่องจริงจะเพิ่มพื้นที่ว่าง : D
รหัสต่อไปนี้นำมาจากเมธอด assertGC (... ) มันพยายามบังคับให้ตัวรวบรวมขยะ nondeterministic รวบรวม
List<byte[]> alloc = new ArrayList<byte[]>();
int size = 100000;
for (int i = 0; i < 50; i++) {
if (ref.get() == null) {
// Test succeeded! Week referenced object has been cleared by gc.
return;
}
try {
System.gc();
} catch (OutOfMemoryError error) {
// OK
}
try {
System.runFinalization();
} catch (OutOfMemoryError error) {
// OK
}
// Approach the jvm maximal allocatable memory threshold
try {
// Allocates memory.
alloc.add(new byte[size]);
// The amount of allocated memory is increased for the next iteration.
size = (int)(((double)size) * 1.3);
} catch (OutOfMemoryError error) {
// The amount of allocated memory is decreased for the next iteration.
size = size / 2;
}
try {
if (i % 3 == 0) Thread.sleep(321);
} catch (InterruptedException t) {
// ignore
}
}
// Test failed!
// Free resources required for testing
alloc = null;
// Try to find out who holds the reference.
String str = null;
try {
str = findRefsFromRoot(ref.get(), rootsHint);
} catch (Exception e) {
throw new AssertionFailedErrorException(e);
} catch (OutOfMemoryError err) {
// OK
}
fail(text + ":\n" + str);
แหล่งที่มา (ฉันเพิ่มความคิดเห็นเพื่อความชัดเจน): ตัวอย่าง NbTestCase
คุณสามารถลองใช้Runtime.getRuntime().gc()
หรือใช้วิธีอรรถประโยชน์System.gc()
หมายเหตุ: วิธีการเหล่านี้ไม่แน่ใจว่า GC และขอบเขตของพวกเขาควร จำกัด อยู่ที่ JVM แทนที่จะจัดการด้วยโปรแกรมในแอปพลิเคชันของคุณ
หากคุณใช้ JUnit และ Spring ลองเพิ่มสิ่งนี้ในทุก ๆ คลาสการทดสอบ:
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)