Khi nào thì System.gc () làm điều gì đó?


118

Tôi biết rằng việc thu gom rác được tự động hóa trong Java. Nhưng tôi hiểu rằng nếu bạn gọi System.gc()trong mã của mình, JVM có thể quyết định hoặc không thực hiện thu gom rác tại thời điểm đó. Làm thế nào để điều này hoạt động chính xác? Chính xác thì dựa trên cơ sở / thông số nào mà JVM quyết định làm (hoặc không làm) một GC khi nó thấy System.gc()?

Có bất kỳ ví dụ nào trong trường hợp đó là một ý tưởng hay để đưa điều này vào mã của bạn không?


4
Quá phức tạp về một khái niệm đến mức đầy đủ chi tiết ở đây, nhưng bài viết này sẽ giúp bạn: messjava.com/posts/how-does-garbage-collection-work
GEOCHET 15/09/08

Hành vi chính xác phụ thuộc vào JVM, tức là phụ thuộc vào việc triển khai.
Thorbjørn Ravn Andersen vào

Câu trả lời:


59

Trong thực tế, nó thường quyết định thu gom rác. Câu trả lời khác nhau tùy thuộc vào nhiều yếu tố, như JVM bạn đang chạy, chế độ đó ở chế độ nào và thuật toán thu gom rác mà nó đang sử dụng.

Tôi sẽ không phụ thuộc vào nó trong mã của bạn. Nếu JVM sắp ném OutOfMemoryError, việc gọi System.gc () sẽ không ngăn chặn nó, bởi vì bộ thu gom rác sẽ cố gắng giải phóng nó nhiều nhất có thể trước khi nó đi đến mức cực đoan. Lần duy nhất tôi thấy nó được sử dụng trong thực tế là trong IDE nơi nó được gắn vào một nút mà người dùng có thể nhấp vào, nhưng ngay cả ở đó nó cũng không hữu ích lắm.


3
bạn có thể bắt buộc thu gom rác trong jconsole
Benedikt Waldvogel 15/09/08

25
@bene Bạn có thực sự "ép" được không? Jconsole chỉ gọi System.gc ()?
John Meagher,

4
Tôi sẽ sử dụng System.gc()khi đang ở màn hình tải trò chơi điện tử. Tại thời điểm đó, tôi sẽ không thực sự quan tâm nếu cuộc gọi xóa mọi thứ có thể, hay không làm gì cả. Tuy nhiên, tôi thích nó "dành nỗ lực để tái chế những đồ vật không sử dụng" trong màn hình tải hơn là trong khi chơi trò chơi.
Kröw

30

Ví dụ duy nhất tôi có thể nghĩ về nơi hợp lý khi gọi System.gc () là khi lập hồ sơ một ứng dụng để tìm kiếm rò rỉ bộ nhớ có thể xảy ra. Tôi tin rằng những người lập hồ sơ gọi phương pháp này ngay trước khi chụp nhanh bộ nhớ.


4
Vâng, đó là những gì tôi làm. Các giá trị được trả về bởi Runtime.freeMemory () chẳng hạn chỉ thực sự có ý nghĩa sau System.gc (), bởi vì thông thường bạn muốn biết có bao nhiêu bộ nhớ bị chặn bởi các cá thể không thể thu thập. Tất nhiên, chỉ được sử dụng trong gỡ lỗi / cấu hình bộ nhớ, không bao giờ được sản xuất.
sleske

Không, nó cũng tốt cho nhiều kịch bản khác. Ví dụ: giả sử bạn có một mẫu duy nhất nhận biết được vòng đời. Trong onStop (), nếu bạn là null (ing), bạn nên gọi System.gc () để trợ giúp.
portfoliobuilder

26

Đặc tả ngôn ngữ Java không đảm bảo rằng JVM sẽ bắt đầu GC khi bạn gọi System.gc(). Đây là lý do của việc này "có thể hoặc không thể quyết định thực hiện GC tại thời điểm đó".

Bây giờ, nếu bạn nhìn vào mã nguồn OpenJDK , là xương sống của Oracle JVM, bạn sẽ thấy rằng một lệnh gọi System.gc()bắt đầu một chu trình GC. Nếu bạn sử dụng JVM khác, chẳng hạn như J9, bạn phải kiểm tra tài liệu của họ để tìm ra câu trả lời. Ví dụ: JVM của Azul có một bộ thu gom rác chạy liên tục, vì vậy một lệnh gọi System.gc()sẽ không làm được gì cả

Một số câu trả lời khác đề cập đến việc bắt đầu GC trong JConsole hoặc VisualVM. Về cơ bản, các công cụ này thực hiện cuộc gọi từ xa tới System.gc().

Thông thường, bạn không muốn bắt đầu một chu trình thu thập rác từ mã của mình, vì nó làm rối tung ngữ nghĩa của ứng dụng của bạn. Ứng dụng của bạn thực hiện một số công việc kinh doanh, JVM sẽ quản lý bộ nhớ. Bạn nên tách những mối quan tâm đó ra (đừng bắt ứng dụng của bạn thực hiện một số công việc quản lý bộ nhớ, hãy tập trung vào kinh doanh).

Tuy nhiên, có một số trường hợp mà một cuộc gọi đến System.gc()có thể hiểu được. Hãy xem xét, ví dụ, microbenchmarks. Không ai muốn có một chu trình GC xảy ra ở giữa một microbenchmark. Vì vậy, bạn có thể kích hoạt chu kỳ GC giữa mỗi lần đo để đảm bảo mọi phép đo đều bắt đầu với một đống trống.


26

Bạn không có quyền kiểm soát GC trong java - VM quyết định. Tôi chưa bao giờ chạy ngang qua một trường hợp System.gc()cần thiết . Vì một System.gc()cuộc gọi chỉ đơn giản là SUGGESTS rằng máy ảo thực hiện thu gom rác và nó cũng thực hiện thu thập rác ĐẦY ĐỦ (các thế hệ cũ và mới trong một đống đa thế hệ), nên nó thực sự có thể gây ra NHIỀU chu kỳ cpu được tiêu thụ hơn mức cần thiết.

Trong một số trường hợp, có thể hợp lý khi đề xuất với VM rằng nó thực hiện thu thập đầy đủ NGAY BÂY GIỜ vì bạn có thể biết rằng ứng dụng sẽ không hoạt động trong vài phút tới trước khi xảy ra quá trình tải nặng. Ví dụ: ngay sau khi khởi tạo rất nhiều đối tượng tạm thời trong quá trình khởi động ứng dụng (tức là tôi chỉ lưu vào bộ nhớ cache TON thông tin và tôi biết mình sẽ không nhận được nhiều hoạt động trong một phút hoặc lâu hơn). Hãy nghĩ về một IDE chẳng hạn như eclipse khởi động - nó cần rất nhiều thứ để khởi tạo, vì vậy có lẽ ngay sau khi khởi tạo, bạn nên thực hiện một gc đầy đủ tại thời điểm đó.


17

Bạn cần phải rất cẩn thận nếu bạn gọi điện System.gc(). Gọi nó có thể thêm các vấn đề hiệu suất không cần thiết vào ứng dụng của bạn và nó không được đảm bảo thực sự thực hiện một bộ sưu tập. Thực sự có thể vô hiệu hóa rõ ràng System.gc()thông qua đối số java -XX:+DisableExplicitGC.

Tôi thực sự khuyên bạn nên đọc qua các tài liệu có sẵn tại Java HotSpot Garbage Collection để biết thêm chi tiết chuyên sâu về việc thu gom rác.


Ứng dụng Android của tôi luôn ném cho tôi một OutOfMemoryException. Vì vậy, tôi đã nghĩ đến việc sử dụng System.gc () trong hàm onDestroy của lớp mình để xóa tất cả các tham chiếu và đối tượng không mong muốn. Đó có phải là một cách tiếp cận tốt
Sagar Devanga

2
@Sagar Devanga Gọi gc theo cách thủ công như bạn mô tả không có khả năng hữu ích. Trước khi ném một oom, JVM sẽ đã đã cố gắng thu thập rác để giải phóng bộ nhớ
Scrubbie

10

System.gc()được thực hiện bởi VM và những gì nó làm là việc triển khai cụ thể. Ví dụ, người triển khai chỉ có thể quay lại và không làm gì cả.

Về thời điểm phát hành bộ sưu tập thủ công, thời điểm duy nhất khi bạn có thể muốn làm điều này là khi bạn từ bỏ một bộ sưu tập lớn chứa vô số bộ sưu tập nhỏ hơn - Map<String,<LinkedList>> chẳng hạn như - và bạn muốn thử và thực hiện thành công sau đó và ở đó, nhưng phần lớn, bạn không nên lo lắng về điều đó. GC hiểu rõ hơn bạn - đáng buồn thay - hầu hết thời gian.


8

Nếu bạn sử dụng bộ đệm bộ nhớ trực tiếp, JVM sẽ không chạy GC cho bạn ngay cả khi bạn sắp hết bộ nhớ trực tiếp.

Nếu bạn gọi ByteBuffer.allocateDirect()và nhận được OutOfMemoryError, bạn có thể thấy cuộc gọi này vẫn ổn sau khi kích hoạt GC theo cách thủ công.


Có phải bạn đang nói rằng System.gc () thu thập các phân bổ trực tiếp trong khi JVM thông thường thì không (trước khi ném OOM). Bởi vì tôi nghĩ rằng điều đó là sai. Lý do duy nhất để phân bổ có thể thành công sau khi GC giải thích là thời gian bổ sung và một số likellyhood hơn của một đối tượng trong đống với một trình hoàn thiện đang được giải phóng (và giải phóng một số đối tượng được phân bổ trực tiếp).
eckes 13/12/12

Trong phiên bản mới nhất của Java 6, mã trong Bộ đệm phân bổ sẽ luôn chạy System.gc () trước khi xuất hiện Lỗi OutOfMemoryError. Trong mã hoàn thiện, nó có một đoạn ngắn Cleanersmà bộ nhớ trực tiếp sử dụng. tức là nó không được giải phóng bởi chuỗi cuối cùng vì điều này có thể quá chậm. Mặc dù vậy, vẫn có thể nhận được OOME nếu bạn đang tạo các vùng bộ nhớ trực tiếp đặc biệt nhanh. Giải pháp là không làm điều đó và sử dụng lại các vùng bộ nhớ trực tiếp của bạn nơi bạn có thể.
Peter Lawrey 13/12/12

1
cảm ơn, điều này có vẻ giống như kinh nghiệm của tôi. Vì vậy, tôi đoán câu trả lời chỉ đúng cho các phiên bản Java cũ hơn.
eckes 14/12/12

5

Hầu hết các JVM sẽ khởi động GC (tùy thuộc vào -XX: DiableExplicitGC và -XX: + ExplicitGCInvokesConcurrent switch). Nhưng đặc điểm kỹ thuật chỉ được xác định ít hơn để cho phép triển khai tốt hơn sau này.

Thông số kỹ thuật cần được làm rõ: Lỗi # 6668279: (spec) System.gc () sẽ cho biết rằng chúng tôi không khuyến khích sử dụng và không đảm bảo hành vi

Nội bộ phương pháp gc được sử dụng bởi RMI và NIO và chúng yêu cầu thực thi đồng bộ, điều này hiện đang được thảo luận:

Lỗi # 5025281: Cho phép System.gc () kích hoạt các bộ sưu tập đầy đủ đồng thời (không dừng lại trên thế giới)


3

Garbage Collectionlà tốt Java, nếu chúng tôi đang thực thi Phần mềm được mã hóa bằng java trong Máy tính để bàn / máy tính xách tay / máy chủ. Bạn có thể gọi System.gc()hoặc Runtime.getRuntime().gc()đến Java.

Chỉ cần lưu ý rằng không có cuộc gọi nào trong số đó được đảm bảo thực hiện bất kỳ điều gì. Chúng chỉ là một gợi ý để jvm chạy Trình thu gom rác. Nó phụ thuộc vào JVM cho dù nó có chạy GC hay không. Vì vậy, câu trả lời ngắn gọn: chúng tôi không biết khi nào nó chạy. Câu trả lời dài hơn: JVM sẽ chạy gc nếu có thời gian cho việc đó.

Tôi tin rằng, điều tương tự cũng áp dụng cho Android. Tuy nhiên, điều này có thể làm chậm hệ thống của bạn.


JVM sẽ chạy gc nếu có thời gian : không phải lúc nào cũng đúng, Jvm có thể chạy gc khi bộ nhớ Eden đầy.
Abdelmouhendum

2

Thông thường, máy ảo sẽ tự động thu thập rác trước khi ném OutOfMemoryException, vì vậy việc thêm một lệnh gọi rõ ràng sẽ không giúp ích được gì ngoại trừ việc nó có thể di chuyển lần truy cập hiệu suất đến một thời điểm sớm hơn.

Tuy nhiên, tôi nghĩ rằng tôi đã gặp một trường hợp mà nó có thể có liên quan. Tuy nhiên, tôi không chắc lắm, vì tôi vẫn chưa kiểm tra xem nó có ảnh hưởng gì không:

Khi bạn ánh xạ bộ nhớ một tệp, tôi tin rằng lệnh gọi map () ném IOException khi không có sẵn một khối bộ nhớ đủ lớn. Tôi nghĩ rằng một bộ sưu tập rác ngay trước tệp map () có thể giúp ngăn chặn điều đó. Bạn nghĩ sao?


2

chúng ta không bao giờ có thể bắt buộc thu gom rác. System.gc chỉ đề xuất vm để thu gom rác, tuy nhiên, thực sự thì cơ chế này chạy vào thời gian nào thì không ai biết, điều này được nêu trong các đặc tả JSR.


2

Có rất nhiều điều phải nói khi dành thời gian để thử nghiệm các cài đặt thu gom rác khác nhau, nhưng như đã đề cập ở trên, việc làm như vậy thường không hữu ích.

Tôi hiện đang thực hiện một dự án liên quan đến một môi trường giới hạn bộ nhớ và một lượng dữ liệu tương đối lớn - có một số phần lớn dữ liệu đã đẩy môi trường của tôi đến giới hạn của nó và mặc dù tôi đã có thể giảm mức sử dụng bộ nhớ xuống. rằng về lý thuyết nó sẽ hoạt động tốt, tôi vẫn sẽ gặp lỗi không gian đống --- các tùy chọn GC dài dòng cho tôi thấy rằng nó đang cố gắng thu thập rác, nhưng vô ích. Trong trình gỡ lỗi, tôi có thể thực hiện System.gc () và chắc chắn rằng sẽ có "rất nhiều" bộ nhớ khả dụng ... không phải là nhiều, nhưng đủ.

Do đó, lần duy nhất ứng dụng của tôi gọi System.gc () là khi nó sắp nhập phân đoạn mã nơi các bộ đệm lớn cần thiết để xử lý dữ liệu sẽ được phân bổ và một bài kiểm tra trên bộ nhớ trống có sẵn cho thấy rằng tôi không đảm bảo có nó. Đặc biệt, tôi đang xem xét môi trường 1gb, nơi dữ liệu tĩnh chiếm ít nhất 300mb, với phần lớn dữ liệu không tĩnh liên quan đến thực thi ngoại trừ khi dữ liệu đang được xử lý có ít nhất 100-200 MB tại nguồn. Tất cả đều là một phần của quy trình chuyển đổi dữ liệu tự động, do đó, tất cả dữ liệu đều tồn tại trong khoảng thời gian tương đối ngắn về lâu dài.

Thật không may, mặc dù thông tin về các tùy chọn khác nhau để điều chỉnh bộ thu gom rác có sẵn, nhưng có vẻ như phần lớn là một quá trình thử nghiệm và các chi tiết cụ thể ở cấp độ thấp hơn cần thiết để hiểu cách xử lý các tình huống cụ thể này không dễ dàng có được.

Tất cả những điều đã nói, mặc dù tôi đang sử dụng System.gc (), tôi vẫn tiếp tục điều chỉnh bằng cách sử dụng các tham số dòng lệnh và cố gắng cải thiện thời gian xử lý tổng thể của ứng dụng của mình một lượng tương đối đáng kể, mặc dù không thể vượt qua vấp ngã do làm việc với các khối dữ liệu lớn hơn. Điều đó đang được nói, System.gc () là một công cụ .... một công cụ rất không đáng tin cậy, và nếu bạn không cẩn thận với cách bạn sử dụng nó, bạn sẽ ước rằng nó không hoạt động thường xuyên hơn không.


2

Nếu bạn muốn biết liệu của mình System.gc()có được gọi hay không, bạn có thể nhận thông báo với Java 7 bản cập nhật 4 mới khi JVM thực hiện Thu dọn rác.

Mặc dù vậy, tôi không chắc chắn 100% rằng lớp GarbageCollectorMXBean đã được giới thiệu trong bản cập nhật Java 7 4, vì tôi không thể tìm thấy nó trong ghi chú phát hành, nhưng tôi đã tìm thấy thông tin trên trang web javaperformancetuning .com


0

Tôi không thể nghĩ ra một ví dụ cụ thể khi chạy GC rõ ràng là tốt.

Nói chung, việc chạy GC rõ ràng thực sự có thể gây hại nhiều hơn lợi, vì một gc rõ ràng sẽ kích hoạt một bộ sưu tập đầy đủ, mất nhiều thời gian hơn đáng kể khi nó đi qua mọi đối tượng. Nếu gc rõ ràng này kết thúc được gọi nhiều lần, nó có thể dễ dàng dẫn đến ứng dụng chạy chậm vì tốn nhiều thời gian để chạy các GC đầy đủ.

Ngoài ra, nếu xem qua heap bằng trình phân tích heap và bạn nghi ngờ một thành phần thư viện đang gọi GC rõ ràng, bạn có thể tắt nó bằng cách thêm: gc = -XX: + DisableExplicitGC vào các tham số JVM.


2
Chúng tôi gọi System.gc () để cố gắng đóng các đối tượng MappedByteBuffer của chúng tôi, các đối tượng này không được đóng và do đó không khả dụng cho các quy trình khác hoặc để cắt bớt tệp, mặc dù chúng tôi gọi 'close ()' trên các đối tượng. Thật không may, nó vẫn không phải lúc nào cũng đóng chúng.
Tim Cooper

0

Chuyển sang Tư duy trong Java của Bruce Eckel, một trường hợp sử dụng cho lời gọi System.gc () rõ ràng là khi bạn muốn bắt buộc hoàn thiện, tức là gọi phương thức hoàn tất .


NB: có một phương pháp riêng để buộc chạy quá trình hoàn thiện. (Vấn đề thực sự không nằm ở việc chạy trình hoàn thiện mà là nhận ra rằng một đối tượng có thể được hoàn thiện vì nó không được tham chiếu)
eckes 13/12/12

-1

trong khi system.gc hoạt động, nó sẽ dừng thế giới: tất cả các lần hồi sinh đều bị dừng lại để bộ thu gom rác có thể quét mọi đối tượng để kiểm tra xem nó có cần xóa hay không. nếu ứng dụng là một dự án web, tất cả yêu cầu sẽ bị dừng cho đến khi gc kết thúc và điều này sẽ khiến dự án web của bạn không thể hoạt động trong một khoản tiền.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.