Trình thu gom rác trong Android


108

Tôi đã thấy nhiều câu trả lời của Android đề xuất gọi trình thu gom rác trong một số tình huống.

Yêu cầu trình thu gom rác trong Android trước khi thực hiện một thao tác ngốn bộ nhớ có phải là một phương pháp hay không? Nếu không, tôi chỉ nên gọi nó nếu tôi gặp OutOfMemorylỗi?

Có những thứ khác tôi nên sử dụng trước khi nhờ đến người thu gom rác không?

Câu trả lời:


144

Đối với các phiên bản trước 3.0 honeycomb : Có, thực hiện cuộc gọi System.gc() .

Tôi đã cố gắng tạo Bitmap, nhưng luôn gặp "lỗi máy ảo hết bộ nhớ". Nhưng, khi tôi gọi System.gc()trước, nó không sao.

Khi tạo bitmap, Android thường không thành công với lỗi hết bộ nhớ và không cố gắng dọn rác trước . Do đó, hãy gọi System.gc()và bạn có đủ bộ nhớ để tạo Bitmap.

Nếu tạo Đối tượng, tôi nghĩ System.gcsẽ được gọi tự động nếu cần, nhưng không phải để tạo bitmap. Nó chỉ thất bại.

Vì vậy, tôi khuyên bạn nên gọi thủ công System.gc()trước khi tạo bitmap.


39
Điều này là do dữ liệu bitmap, precomb không được lưu trữ trong máy ảo và vì vậy nó sẽ không kích hoạt GC. Nó không phải là một vấn đề trong Honeycomb và sau đó.
Timmmm

2
@Timmmm nhưng đó thực sự là vấn đề của tôi, tôi chỉ đặt bigheap thành true.
Lei Leyba

118

Nói chung, với sự hiện diện của người thu gom rác, việc gọi GC theo cách thủ công không bao giờ là cách tốt. GC được tổ chức xung quanh các thuật toán heuristic hoạt động tốt nhất khi được để cho các thiết bị của riêng chúng. Việc gọi GC theo cách thủ công thường làm giảm hiệu suất.

Đôi khi , trong một số tình huống tương đối hiếm, người ta có thể thấy rằng một GC cụ thể làm sai và một lệnh gọi thủ công tới GC sau đó có thể cải thiện mọi thứ, về mặt hiệu suất. Điều này là do không thực sự có thể triển khai một GC "hoàn hảo" để quản lý bộ nhớ một cách tối ưu trong mọi trường hợp. Những tình huống như vậy rất khó dự đoán và phụ thuộc vào nhiều chi tiết thực hiện tinh vi. "Thực hành tốt" là để cho GC tự điều hành; một lệnh gọi thủ công tới GC là ngoại lệ, chỉ nên được hình dung sau khi vấn đề hiệu suất thực tế đã được chứng kiến ​​hợp lệ.


8
+1 cho một câu trả lời tốt độc lập với nền tảng. Chờ xem liệu ai đó có đưa ra câu trả lời dành riêng cho Android hay không trước khi chọn.
hpique

8
Tôi đồng ý với những gì bạn đã viết nếu bạn cho phép "hiệu suất" có nghĩa là một cái gì đó chung chung hơn hiệu quả của CPU. Trên thiết bị Android, nhận thức của người dùng về hiệu suất là điều tối quan trọng. Nhà phát triển có thể thích chạy GC trong khi người dùng đang nhấn các nút chẳng hạn, vì vậy người dùng sẽ không biết về GC.
Chủ tịch James K. Polk

5
Trong các trò chơi, điều quan trọng là GC không chạy trong khi trò chơi đang chạy (điều này yêu cầu tất cả các đối tượng được tạo trước và tái chế hoặc tương tự), nhưng khi trò chơi bị tạm dừng là thời điểm tốt để GC.
Pat

4
Dữ liệu bitmap trước tổ ong không được lưu trữ trong bộ nhớ máy ảo. Hệ thống sẽ không phát hiện khi bạn đã sử dụng quá nhiều bộ nhớ không phải VM do bitmap và chạy GC (sẽ chạy các Bitmap.finalize()phương pháp giải phóng bộ nhớ không phải VM). Vì vậy, trong trường hợp này, bạn nên đi qua đầu của GC và chạy Bitmap.recycle()hoặc System.gc()khi thích hợp. Nhưng chỉ có tiền tổ ong.
Timmmm

1
@ThomasPornin - Mặt khác, là một lập trình viên ứng dụng, bạn biết điều gì đó mà hệ điều hành không biết: thời điểm mà ứng dụng của bạn hiện sẽ thực hiện một thay đổi lớn về cách sử dụng bộ nhớ và nó sẽ ít gây rối hơn trải nghiệm người dùng tạm dừng ngay bây giờ, thay vì vào một thời điểm tùy ý trong tương lai . Điều này có nghĩa là có thể hợp lý khi thực hiện các cuộc gọi không thường xuyên , vào những thời điểm chuyển đổi lớn trong cách ứng dụng đang được sử dụng. Những điều này hiếm khi xảy ra theo nghĩa là người ta không nên gọi GC thường xuyên, nhưng phổ biến là hầu như bất kỳ ứng dụng quan trọng nào cũng có những khoảnh khắc thiết kế được biết đến như vậy.
ToolmakerSteve

26

Hết bộ nhớ trong ứng dụng android là rất phổ biến nếu chúng ta không xử lý bitmap đúng cách, Giải pháp cho vấn đề sẽ là

if(imageBitmap != null) {
    imageBitmap.recycle();
    imageBitmap = null;
}
System.gc();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;
imageBitmap = BitmapFactory.decodeFile(URI, options);
Bitmap  scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, 200, 200, true);
imageView.setImageBitmap(scaledBitmap);

Trong đoạn mã trên, Bạn vừa thử tái chế bitmap sẽ cho phép bạn giải phóng không gian bộ nhớ đã sử dụng, vì vậy việc hết bộ nhớ có thể không xảy ra. Tôi đã thử nó hiệu quả với tôi.

Nếu vẫn gặp sự cố, bạn cũng có thể thêm những dòng này

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];
options.inPurgeable = true;

để biết thêm thông tin, hãy xem liên kết này

https://web.archive.org/web/20140514092802/http://voices.yahoo.com/android-virtual-machine-vm-out-memory-error-7342266.html?cat=59


LƯU Ý: Do "tạm dừng" tạm thời do thực hiện gc, bạn không nên thực hiện việc này trước mỗi lần phân bổ bitmap.

Thiết kế tối ưu là:

  1. Giải phóng tất cả các ảnh bitmap không còn cần thiết , bằng if / recycle / nullmã hiển thị. (Đưa ra một phương pháp để trợ giúp điều đó.)

  2. System.gc();

  3. Phân bổ các bitmap mới.


19

Nếu bạn gặp lỗi OutOfMemoryError thì thường là quá muộn để gọi bộ thu gom rác ...

Đây là trích dẫn từ Nhà phát triển Android:

Hầu hết thời gian, việc thu gom rác xảy ra do hàng tấn vật thể nhỏ, tồn tại trong thời gian ngắn và một số trình thu gom rác, như những người thu gom rác thế hệ, có thể tối ưu hóa việc thu thập các vật thể này để ứng dụng không bị gián đoạn quá thường xuyên. Rất tiếc, trình thu gom rác của Android không thể thực hiện các tối ưu hóa như vậy và việc tạo ra các đối tượng tồn tại trong thời gian ngắn trong các đường dẫn mã quan trọng về hiệu suất do đó rất tốn kém cho ứng dụng của bạn.

Vì vậy, theo sự hiểu biết của tôi, không có nhu cầu khẩn cấp để gọi gc. Tốt hơn là bạn nên dành nhiều nỗ lực hơn để tránh việc tạo các đối tượng không cần thiết (như tạo các đối tượng bên trong các vòng lặp)


6
Không thể giải thích câu trích dẫn theo cách khác? Có thể GC cần được gọi theo cách thủ công để thu thập các đối tượng đó trước khi chúng chiếm quá nhiều bộ nhớ.
hpique

@hgpc: Tôi không thấy làm thế nào để trích dẫn có thể được giải thích theo cách bạn đề xuất. Tôi đoán tài liệu là một lời thú nhận, thừa nhận rằng GC của họ rất đơn giản; khi bộ nhớ sắp hết, một GC đầy đủ sẽ được thực thi.
Chủ tịch James K. Polk

Việc sử dụng đầy đủ các đối tượng phức tạp và các khuôn khổ sử dụng các đối tượng phức tạp, có xu hướng sử dụng thời gian phát triển tốt hơn là tối ưu hóa quá sớm. Lo lắng về việc tối ưu hóa là một cái bẫy dễ rơi vào đối với Android hơn là Enterprise Java vì chúng ta luôn nghĩ về hiệu suất trong khi mã hóa một ứng dụng di động trong tiềm thức. Đặc biệt là các nhà phát triển khuôn khổ, những người phải suy nghĩ về hiệu suất, rò rỉ quan điểm đó vào tài liệu chính thức của họ khi họ không cẩn thận.
BênFractal

9

Nó có vẻ System.gc()không hoạt động trên Art Android 6.0.1 Nexus 5x, vì vậy tôi sử dụng Runtime.getRuntime().gc();thay thế.


1
System.gc()là một hàm trình bao bọc cho Runtime.getRuntime().gc(). Xem android.googlesource.com/platform/libcore/+/…
Nathan F.

Vâng, từ nguồn và tài liệu có vẻ như chúng giống nhau, nhưng thực sự System.gc()không hiệu quả với tôi nhưng Runtime.getRuntime().gc()có!
Ivan

7

Ứng dụng của tôi quản lý rất nhiều hình ảnh và nó bị lỗi OutOfMemoryError. Điều này đã giúp tôi. Trong Manifest.xml Thêm

<application
....
   android:largeHeap="true"> 

9
Google không thích điều này
Pedro Paulo Amorim

1
@PedroPauloAmorim giải thích tại sao?
Machado

2
Không sử dụng LargeHeap trừ khi bạn chắc chắn mình đang làm gì. Việc sử dụng các đống lớn làm cho Garbage collector làm việc khó hơn nhiều vì nó phải lặp qua nhiều dữ liệu rác trước khi xóa bộ nhớ.
Prakash

7

Nói chung, bạn không nên gọi GC một cách rõ ràng bằng System.gc (). Thậm chí còn có bài giảng IO ( http://www.youtube.com/watch?v=_CruQY55HOk ), nơi họ giải thích ý nghĩa của nhật ký GC tạm dừng và trong đó họ cũng tuyên bố không bao giờ gọi System.gc () vì Dalvik hiểu rõ hơn hơn bạn khi làm như vậy.

Mặt khác, như đã đề cập trong các câu trả lời ở trên, quy trình GC trong Android (giống như mọi thứ khác) đôi khi có lỗi. Điều này có nghĩa là các thuật toán Dalvik GC không ngang bằng với Hotspot hoặc JRockit JVM và có thể xảy ra sai sót trong một số trường hợp. Một trong những trường hợp đó là khi phân bổ các đối tượng bitmap. Đây là một cách khó vì nó sử dụng bộ nhớ Heap và Non Heap và vì một phiên bản lỏng lẻo của đối tượng bitmap trên thiết bị hạn chế bộ nhớ là đủ để cung cấp cho bạn một ngoại lệ OutOfMemory. Vì vậy, gọi nó sau khi bạn không cần bitmap này nữa - thường được nhiều nhà phát triển đề xuất và thậm chí được một số người coi là phương pháp hay.

Thực tiễn tốt hơn là sử dụng .recycle () trên một bitmap vì nó là những gì phương pháp này được tạo ra, vì nó đánh dấu bộ nhớ gốc của bitmap là an toàn để xóa. Hãy nhớ rằng điều này rất phụ thuộc vào phiên bản, có nghĩa là nó thường được yêu cầu trên các phiên bản Android cũ hơn (Tôi nghĩ là Pre 3.0) nhưng sẽ không bắt buộc trên các phiên bản mới hơn. Ngoài ra, nó sẽ không ảnh hưởng nhiều khi sử dụng nó trên các phiên bản ether mới hơn (chỉ cần không làm điều này theo vòng lặp hoặc điều gì đó tương tự). Thời gian chạy ART mới đã thay đổi rất nhiều ở đây vì họ đã giới thiệu "phân vùng" Heap đặc biệt cho các đối tượng lớn nhưng tôi nghĩ sẽ không gây hại nhiều khi làm điều này với ART ether.

Ngoài ra, một lưu ý rất quan trọng về System.gc (). Phương thức này không phải là lệnh mà Dalvik (hoặc JVM) bắt buộc phải phản hồi. Hãy xem nó giống như nói với máy ảo "Bạn có thể vui lòng thu gom rác nếu nó không phức tạp".



3

Tôi sẽ nói không, vì Nhà phát triển tài liệu về trạng thái sử dụng RAM :

...
GC_EXPLICIT

GC rõ ràng, chẳng hạn như khi bạn gọi gc () (mà bạn nên tránh gọi và thay vào đó tin tưởng GC chạy khi cần).

...

Tôi đã tô đậm phần có liên quan.

Hãy nhìn vào loạt YouTube, Performance Android Patterns - nó sẽ hiển thị cho bạn lời khuyên về quản lý sử dụng bộ nhớ của ứng dụng (chẳng hạn như sử dụng Android của ArrayMaps và SparseArrays thay vì HashMaps).


3

Ghi chú nhanh cho các nhà phát triển Xamarin .

Nếu bạn muốn gọi System.gc()trong Xamarin. Ứng dụng Android bạn nên gọiJava.Lang.JavaSystem.Gc()


2

Không cần phải gọi người thu gom rác sau khi an OutOfMemoryError.

Đó là Javadoc tuyên bố rõ ràng:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

Vì vậy, trình thu gom rác đã cố gắng giải phóng bộ nhớ trước khi tạo ra lỗi nhưng không thành công.


8
Android Javadoc không nói rõ điều đó và rất có thể cách triển khai của nó là khác. d.android.com/reference/java/lang/OutOfMemoryError.html
hpique

Bạn đúng rằng điều này không áp dụng trên Android. Nhưng một lần nữa, bạn không thể xử lý OOE trong ether thời gian chạy, vì vậy bạn không thể làm gì nhiều về nó ngay cả khi điều này là / không đúng.
Igor Čordaš
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.