Có thể buộc thu gom rác trong Java, ngay cả khi nó khó thực hiện không? Tôi biết về System.gc();
và Runtime.gc();
nhưng họ chỉ đề nghị làm GC. Làm thế nào tôi có thể buộc GC?
Có thể buộc thu gom rác trong Java, ngay cả khi nó khó thực hiện không? Tôi biết về System.gc();
và Runtime.gc();
nhưng họ chỉ đề nghị làm GC. Làm thế nào tôi có thể buộc GC?
Câu trả lời:
Tùy chọn tốt nhất của bạn là gọi System.gc()
đơn giản là một gợi ý cho trình thu gom rác mà bạn muốn nó thực hiện một bộ sưu tập. Không có cách nào để buộc và thu gom ngay lập tức vì bộ thu gom rác là không xác định.
non-deterministic == trouble
GC.Collect()
không thu thập. Trong Java gc()
nào.
Các thư viện jlibs có một lớp tiện ích tốt cho thu gom rác thải . Bạn có thể buộc thu gom rác bằng cách sử dụng một mẹo nhỏ tiện lợi với các đối tượng WeakReference .
RuntimeUtil.gc () từ 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
với a ReferenceQueue
và sau đó bạn sẽ được thông báo sau khi hoàn thành, nhưng vẫn trước khi dọn dẹp. Cuối cùng, ngay cả khi bạn phát hiện thành công rằng bộ nhớ cho đối tượng này đã được lấy lại, nó vẫn có nghĩa là rất ít trong một thế hệ GC như HotSpot. Thông thường nó sẽ trùng với việc dọn dẹp thế hệ trẻ.
System.gc(); System.gc();
nó, nhưng chắc chắn sẽ rất thú vị nếu biết nó có hoạt động tốt hơn thế không. Trong thực tế, chỉ cần in bao nhiêu lần nó System.gc()
sẽ gọi là đủ. Cơ hội để đạt được 2 là khá mỏng.
Cách tốt nhất (nếu không chỉ) để buộc một GC sẽ là viết một JVM tùy chỉnh. Tôi tin rằng các công cụ thu gom rác có thể cắm được nên bạn có thể chỉ cần chọn một trong những triển khai có sẵn và điều chỉnh nó.
Lưu ý: Đây KHÔNG phải là một câu trả lời dễ dàng.
Sử dụng Giao diện công cụ máy ảo Java ™ (JVM TI) , chức năng
jvmtiError ForceGarbageCollection(jvmtiEnv* env)
sẽ "Buộc VM thực hiện một bộ sưu tập rác." JVM TI là một phần của Kiến trúc trình gỡ lỗi nền tảng JavaTM (JPDA) .
CÓ , gần như có thể buộc bạn phải gọi các phương thức theo cùng một thứ tự và đồng thời các phương thức này là:
System.gc ();
System.runFinalization ();
ngay cả khi chỉ là một đối tượng để làm sạch việc sử dụng hai phương thức này cùng một lúc, buộc bộ thu gom rác phải sử dụng finalise()
phương thức của đối tượng không thể truy cập để giải phóng bộ nhớ được gán và thực hiện những gì finalize()
phương thức nêu.
TUY NHIÊN Đó là một cách thực hành khủng khiếp khi sử dụng trình thu gom rác vì việc sử dụng phần mềm này có thể gây ra quá tải cho phần mềm thậm chí còn tệ hơn cả bộ nhớ, trình thu gom rác có luồng riêng không thể kiểm soát cộng tùy thuộc vào thuật toán được sử dụng bởi gc có thể mất nhiều thời gian hơn và được coi là rất kém hiệu quả, bạn nên kiểm tra phần mềm của mình nếu nó tệ nhất với sự trợ giúp của gc vì nó chắc chắn bị hỏng, một giải pháp tốt không phải phụ thuộc vào gc.
LƯU Ý: chỉ cần lưu ý rằng điều này sẽ chỉ hoạt động nếu trong phương thức hoàn thiện không phải là sự phân định lại đối tượng, nếu điều này xảy ra, đối tượng sẽ tiếp tục sống, nó sẽ hồi sinh về mặt kỹ thuật.
gc()
chỉ là một gợi ý để chạy một bộ sưu tập rác. runFinalizers()
chỉ chạy các công cụ hoàn thiện trên các đối tượng "đã bị phát hiện bị loại bỏ". Nếu gc không thực sự chạy, có thể không có vật thể nào như vậy ...
Theo tài liệu về OutOfMemoryError, nó tuyên bố rằng nó sẽ không bị ném trừ khi VM không lấy lại được bộ nhớ sau khi thu gom rác đầy đủ. Vì vậy, nếu bạn tiếp tục phân bổ bộ nhớ cho đến khi bạn gặp lỗi, bạn sẽ buộc phải có một bộ sưu tập rác đầy đủ.
Có lẽ câu hỏi mà bạn thực sự muốn hỏi là "làm thế nào tôi có thể lấy lại ký ức mà tôi nghĩ rằng tôi nên lấy lại bằng cách thu gom rác?"
Để yêu cầu thủ công GC (không phải từ System.gc ()):
.gc là một ứng cử viên để loại bỏ trong các bản phát hành trong tương lai - một Kỹ sư Mặt trời đã từng nhận xét rằng có thể ít hơn hai mươi người trên thế giới thực sự biết cách sử dụng .gc () - Tôi đã thực hiện một số công việc tối qua trong vài giờ cho một trung tâm / quan trọng cấu trúc dữ liệu bằng cách sử dụng dữ liệu được tạo bởi SecureRandom, tại một nơi nào đó vừa qua 40.000 đối tượng, vm sẽ chậm lại như thể nó đã hết con trỏ. Rõ ràng nó đã bị nghẹt thở trên các bảng con trỏ 16 bit và thể hiện hành vi "máy móc không hoạt động" cổ điển.
Tôi đã thử -Xms và cứ thế, cứ lặp đi lặp lại một chút cho đến khi nó chạy đến khoảng 57, xxx một cái gì đó. Sau đó, nó sẽ chạy gc đi từ 57.127 đến 57.128 sau một gc () - với tốc độ phình to mã ở trại Easy Money.
Thiết kế của bạn cần làm lại cơ bản, có thể là một cách tiếp cận cửa sổ trượt.
Bạn có thể kích hoạt một GC từ dòng lệnh. Điều này hữu ích cho lô / crontab:
jdk1.7.0/bin/jcmd <pid> GC.run
Xem :
Đặc tả JVM không nói bất cứ điều gì cụ thể về bộ sưu tập rác. Do đó, các nhà cung cấp có thể tự do thực hiện GC theo cách của họ.
Vì vậy, sự mơ hồ này gây ra sự không chắc chắn trong hành vi thu gom rác. Bạn nên kiểm tra chi tiết JVM của bạn để biết về các cách tiếp cận / thuật toán thu gom rác. Ngoài ra còn có các tùy chọn để tùy chỉnh hành vi là tốt.
Nếu bạn cần buộc thu gom rác, có lẽ bạn nên xem xét cách bạn quản lý tài nguyên. Bạn đang tạo ra các đối tượng lớn vẫn tồn tại trong bộ nhớ? Bạn có đang tạo các đối tượng lớn (ví dụ: các lớp đồ họa) có Disposable
giao diện và không gọi dispose()
khi thực hiện với nó không? Bạn có đang khai báo một cái gì đó ở cấp độ lớp mà bạn chỉ cần trong một phương thức không?
Sẽ tốt hơn nếu bạn mô tả lý do tại sao bạn cần thu gom rác. Nếu bạn đang sử dụng SWT, bạn có thể loại bỏ các tài nguyên như Image
và Font
để giải phóng bộ nhớ. Ví dụ:
Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();
Ngoài ra còn có các công cụ để xác định tài nguyên không bị phát hiện.
Nếu bạn sắp hết bộ nhớ và nhận được một bộ nhớ, OutOfMemoryException
bạn có thể thử tăng lượng không gian heap có sẵn cho java bằng cách bắt đầu chương trình với java -Xms128m -Xmx512m
thay vì chỉ java
. Điều này sẽ cung cấp cho bạn kích thước heap ban đầu là 128Mb và tối đa 512Mb, cao hơn nhiều so với 32Mb / 128Mb tiêu chuẩn.
java -Xms512M -Xmx1024M
Một lựa chọn khác là không tạo đối tượng mới.
Đối tượng tổng hợp là để giảm nhu cầu GC trong Java.
Nhóm đối tượng nói chung sẽ không nhanh hơn việc tạo Đối tượng (đặc biệt đối với các đối tượng nhẹ) nhưng nhanh hơn so với Bộ sưu tập rác. Nếu bạn tạo 10.000 đối tượng và mỗi đối tượng là 16 byte. Đó là 160.000 byte GC phải lấy lại. Mặt khác, nếu bạn không cần tất cả 10.000 cùng một lúc, bạn có thể tạo một nhóm để tái chế / tái sử dụng các đối tượng loại bỏ nhu cầu xây dựng các đối tượng mới và loại bỏ nhu cầu đối với các đối tượng cũ.
Một cái gì đó như thế này (chưa được kiểm tra). Và nếu bạn muốn nó là chủ đề an toàn, bạn có thể trao đổi LinkedList cho một concienLinkedQueue.
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) { }
}
Trên OracleJDK 10 với G1 GC, một lệnh gọi System.gc()
sẽ khiến cho GC dọn sạch Bộ sưu tập cũ. Tôi không chắc chắn nếu chạy ngay lập tức. Tuy nhiên, GC sẽ không dọn sạch Bộ sưu tập trẻ ngay cả khi System.gc()
được gọi nhiều lần trong một vòng lặp. Để có được GC để dọn sạch Bộ sưu tập trẻ, bạn phải phân bổ trong một vòng lặp (ví dụ new byte[1024]
) mà không cần gọi System.gc()
. Gọi System.gc()
cho một số lý do ngăn cản GC làm sạch Bộ sưu tập trẻ.
Thực sự, tôi không hiểu bạn. Nhưng để rõ ràng về "Tạo đối tượng vô hạn" Tôi có nghĩa là có một số đoạn mã trong hệ thống lớn của tôi tạo ra các đối tượng xử lý và tồn tại trong bộ nhớ, tôi thực sự không thể có được đoạn mã này, chỉ là cử chỉ !!
Điều này là chính xác, chỉ cử chỉ. Bạn có khá nhiều câu trả lời tiêu chuẩn đã được đưa ra bởi một số áp phích. Chúng ta hãy thực hiện từng cái một:
Chính xác, không có jvm thực tế - đó chỉ là một đặc điểm kỹ thuật, một nhóm khoa học máy tính mô tả một hành vi mong muốn ... Gần đây tôi đã đào sâu vào việc khởi tạo các đối tượng Java từ mã gốc. Để có được những gì bạn muốn, cách duy nhất là làm những gì được gọi là nulling tích cực. Những sai lầm nếu làm sai là làm xấu đến mức chúng ta phải giới hạn bản thân trong phạm vi ban đầu của câu hỏi:
Hầu hết các áp phích ở đây sẽ cho rằng bạn đang nói rằng bạn đang làm việc với một giao diện, nếu vậy chúng tôi sẽ phải xem liệu bạn có đang trao toàn bộ vật thể hoặc một vật phẩm một lúc không.
Nếu bạn không còn cần một đối tượng, bạn có thể gán null cho đối tượng nhưng nếu bạn hiểu sai, có một ngoại lệ con trỏ null được tạo. Tôi cá là bạn có thể đạt được công việc tốt hơn nếu bạn sử dụng NIO
Bất cứ lúc nào bạn hoặc tôi hoặc bất kỳ ai khác nhận được: " Xin vui lòng tôi cần điều đó khủng khiếp. " Nó gần như là tiền thân của sự phá hủy gần như toàn bộ những gì bạn đang cố gắng làm .... viết cho chúng tôi một mã mẫu nhỏ, khử trùng mã thực tế được sử dụng và cho chúng tôi thấy câu hỏi của bạn.
Đừng nản lòng. Thông thường những gì điều này giải quyết là dba của bạn đang sử dụng một gói được mua ở đâu đó và thiết kế ban đầu không được điều chỉnh cho các cấu trúc dữ liệu lớn.
Điều đó rất phổ biến.
FYI
Phương thức gọi System.runFinalulatorsOnExit (true) đảm bảo rằng các phương thức hoàn thiện được gọi trước khi Java tắt. Tuy nhiên, phương pháp này vốn không an toàn và đã bị phản đối. Một cách khác là thêm hook shutdown của máy móc với một phương thức Runtime.addShutdownHook.
Masarrat Siddiqui
Có một số cách gián tiếp để buộc người thu gom rác. Bạn chỉ cần điền vào đống với các đối tượng tạm thời cho đến thời điểm trình thu gom rác sẽ thực thi. Tôi đã tạo ra lớp học buộc người thu gom rác theo cách này:
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;
}
}
Sử dụng:
GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();
Tôi không biết phương pháp này hữu ích đến mức nào, bởi vì nó liên tục lấp đầy đống, nhưng nếu bạn có ứng dụng quan trọng, thì PHẢI buộc GC - khi đó có thể là cách di động Java để buộc GC.
Tôi muốn thêm một số điều ở đây. Xin lưu ý rằng Java chạy trên Máy ảo và không phải Máy thực tế. Máy ảo có cách giao tiếp riêng với máy. Nó có thể thay đổi từ hệ thống này sang hệ thống khác. Bây giờ Khi chúng tôi gọi cho GC, chúng tôi yêu cầu Máy ảo Java gọi Trình thu gom rác.
Vì Trình thu gom rác với Máy ảo, chúng tôi không thể buộc nó dọn dẹp ở đó và sau đó. Thay vào đó, chúng tôi xếp hàng yêu cầu của chúng tôi với Bộ sưu tập rác. Nó phụ thuộc vào Máy ảo, sau thời gian cụ thể (điều này có thể thay đổi từ hệ thống này sang hệ thống khác, thường là khi bộ nhớ ngưỡng được phân bổ cho JVM đầy) máy thực tế sẽ giải phóng không gian. : D
Đoạn mã sau được lấy từ phương thức assertGC (...). Nó cố gắng buộc người thu gom rác không phá hủy phải thu thập.
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);
Nguồn (Tôi đã thêm một số ý kiến cho rõ ràng): Ví dụ NbTestCase
Bạn có thể thử sử dụng Runtime.getRuntime().gc()
hoặc sử dụng phương thức tiện ích System.gc()
Lưu ý: Các phương pháp này không đảm bảo GC. Và phạm vi của chúng nên được giới hạn ở JVM thay vì xử lý theo chương trình trong ứng dụng của bạn.