2011-10-14 30 views
13

Tôi thấy một vấn đề khá kỳ quặc. Về cơ bản, đôi khi phân bổ bộ nhớ bitmap lớn sẽ thất bại mặc dù có rất nhiều bộ nhớ. Có một số bài đăng xuất hiện để hỏi một câu hỏi tương tự nhưng tất cả đều liên quan đến Android trước tổ ong. Sự hiểu biết của tôi là hình ảnh được phân bổ trên heap bây giờ, thay vì một số bộ nhớ bên ngoài. Dù sao, vui lòng xem nhật ký bên dưới:Lỗi OutOfMemory mặc dù bộ nhớ trống có sẵn

10-14 13:43:53.020: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 40.637MB for 942134-byte allocation 
    10-14 13:43:53.070: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 126K, 11% free 41399K/46343K, paused 31ms 
    10-14 13:43:53.130: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 920K, 13% free 40478K/46343K, paused 30ms 
    10-14 13:43:53.180: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1026K, 13% free 40479K/46343K, paused 30ms 
    10-14 13:43:53.250: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 931K, 12% free 41193K/46343K, paused 31ms 
    10-14 13:43:53.250: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 41.313MB for 1048592-byte allocation 
    10-14 13:43:53.280: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 11% free 42217K/47431K, paused 31ms 
    10-14 13:44:01.520: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 3493K, 15% free 40646K/47431K, paused 3ms+9ms 
    10-14 13:44:08.130: DEBUG/dalvikvm(31533): GC_EXPLICIT freed 16461K, 47% free 25527K/47431K, paused 3ms+6ms 
    10-14 13:44:09.150: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1007K, 45% free 26191K/47431K, paused 35ms 
    10-14 13:44:09.160: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 29.334MB for 3850256-byte allocation 
    10-14 13:44:09.200: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 0K, 37% free 29951K/47431K, paused 2ms+4ms 
    10-14 13:44:11.970: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1878K, 38% free 29784K/47431K, paused 37ms 
    10-14 13:44:12.410: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 62K, 36% free 30441K/47431K, paused 32ms 
    10-14 13:44:12.440: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 32% free 32325K/47431K, paused 32ms 
    10-14 13:44:12.440: INFO/dalvikvm-heap(31533): Forcing collection of SoftReferences for 3850256-byte allocation 
    10-14 13:44:12.480: DEBUG/dalvikvm(31533): GC_BEFORE_OOM freed 124K, 33% free 32200K/47431K, paused 37ms 
    10-14 13:44:12.480: ERROR/dalvikvm-heap(31533): Out of memory on a 3850256-byte allocation. 

Tôi xin lỗi vì đã đăng nhập quá nhiều, tôi hy vọng nó có liên quan. Cách tôi đọc nó là hệ thống liên tục điều chỉnh kích thước heap cho đến khi nó đạt đến mức tối đa heap. Sau đó, chúng tôi yêu cầu phân bổ đặc biệt lớn không thành công. Rõ ràng là có quá đủ bộ nhớ có sẵn (khoảng 15 megs). Điều này có nghĩa là đống nội bộ bị phân mảnh và không có phân đoạn bộ nhớ tiếp giáp đủ lớn để xử lý phân bổ của chúng tôi? Nếu đó là trường hợp tôi nên làm gì? Nếu không phải vậy, thì sao?

Xin cảm ơn trước.

+0

Bạn có đang sử dụng các phần mềm không? – Raunak

+0

Không trực tiếp, mặc dù dòng cuối cùng thứ 3 nói về SoftReferences. Tôi đang sử dụng bộ nhớ cache dựa trên bộ sưu tập google sử dụng WeakReferences theo như tôi biết. – EightyEight

+0

Tôi thấy cùng một vấn đề trên S3 trên OS 4.1.2, với bộ nhớ còn trống hơn: 10-02 14: 28: 22.458 5333 9044 D dalvikvm: GC_BEFORE_OOM giải phóng 0K, 54% miễn phí 30274K/65543K, 120ms bị tạm dừng, tổng số 120ms –

Trả lời

16

Hành vi lạ là vì bitmap được phân bổ trên vùng gốc và không được thu thập rác, nhưng android chỉ có thể theo dõi các đối tượng trên đống rác thu thập được. Từ Android 2.2 (hoặc 2.3 có thể) điều này đã thay đổi và phân bổ bitmap được hiển thị quá nếu bạn thực hiện một bãi chứa đống.

Quay lại câu hỏi, vấn đề của bạn hầu hết là các bitmap bạn tải theo cách thủ công không được giải phóng một cách thích hợp. Một vấn đề điển hình là một số cuộc gọi lại vẫn được đặt hoặc chế độ xem vẫn đang tham chiếu bitmap. Vấn đề phổ biến khác là nếu bạn tải các bitmap lớn theo cách thủ công (không phải là tài nguyên), bạn sẽ cần gọi lại() trên chúng khi bạn không cần nữa, điều này sẽ giải phóng bitmap khỏi bộ nhớ riêng để người thu gom rác sẽ có thể làm việc như nó cần. (GC chỉ thấy các đối tượng trên đống GC, và không có đối tượng nào để giải phóng bộ nhớ miễn phí từ vùng heap gốc, và thậm chí không quan tâm đến nó.)

Tôi có đứa bé nhỏ này tất cả các thời gian:

public static void stripImageView(ImageView view) { 
    if (view.getDrawable() instanceof BitmapDrawable) { 
     ((BitmapDrawable)view.getDrawable()).getBitmap().recycle(); 
    } 
    view.getDrawable().setCallback(null); 
    view.setImageDrawable(null); 
    view.getResources().flushLayoutCache(); 
    view.destroyDrawingCache(); 
} 
+0

Tôi không chắc chắn đó là chính xác cho ong tổ ong. Hình ảnh được phân bổ trên heap ứng dụng tôi tin - dựa trên nội dung nói chuyện trên Google IO 2011 này (trang trình bày 18 http://static.googleusercontent.com/external_content/untrusted_dlcp/www.google.com/en//events/io/2011/ static/presofiles/memory_management_for_android_apps.pdf). Theo giai thoại, tôi thấy đống ứng dụng của tôi tăng lên khi tôi phân bổ một bitmap lớn và thu nhỏ khi tôi phát hành nó (với mã tương tự với những gì bạn đã đăng)> – EightyEight

+1

Yap, tôi đã nói với bạn :) "Từ Android 2.2 (hoặc 2.3 có thể) điều này đã thay đổi ... " – hege

+1

Thật không may điều này dường như không đủ, không chắc tại sao. Recycle/etc dường như không có ảnh hưởng đến kích thước heap (ít nhất là trên thiết bị 4.0 này). –

2

các hình ảnh được lấy từ Web, mỗi từ 300K đến 500K trong kích thước, và lưu trữ trong một ArrayList của Drawables.

Kích thước tệp kb của hình ảnh bạn đang tải từ web không liên quan trực tiếp. Vì chúng được chuyển đổi thành bitmap, bạn cần tính chiều rộng * chiều cao * 4 byte cho mỗi hình ảnh cho hình ảnh ARGB thông thường. (chiều rộng và chiều cao trong px).

Các bitmap tiêu thụ vùng heap gốc, thường không hiển thị trong một hprof. Hprof chỉ hiển thị cho bạn số lượng đối tượng, ví dụ: BitmapDrawables hoặc Bitmap được để lại.

tôi sử dụng mã này trong ứng dụng của tôi để sản xuất các bộ nhớ được sử dụng hiện nay được sử dụng bởi các ứng dụng và đống bản địa:

public static void logHeap(Class clazz) { 
    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576)); 
    Double available = new Double(Debug.getNativeHeapSize())/1048576.0); 
    Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0); 
    DecimalFormat df = new DecimalFormat(); 
    df.setMaximumFractionDigits(2); 
    df.setMinimumFractionDigits(2); 

    Log.d(APP, "debug. ================================="); 
    Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]"); 
    Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)"); 
    System.gc(); 
    System.gc(); 

    // don't need to add the following lines, it's just an app specific handling in my app   
    if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) { 
     android.os.Process.killProcess(android.os.Process.myPid()); 
    } 
} 

mà tôi gọi khi bắt đầu hoặc kết thúc một hoạt động trong phát triển.

logHeap(this.getClass()); 

Dưới đây là một số liên kết thông tin - thường có rất nhiều chủ đề về chủ đề này tại đây.

Đây là cũng một slide hữu ích bởi Romain Guy (Android kỹ sư Framework) về tài liệu tham khảo mềm, tài liệu tham khảo yếu, lưu trữ đơn giản, xử lý hình ảnh: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

+0

Cảm ơn câu trả lời Mehul. Thật không may tất cả các nguồn tài nguyên của bạn có trước Honeycomb. Tôi sẽ kiểm tra cod.e nguồn của bạn – EightyEight

0

có các lỗi "hết bộ nhớ" xảy ra khi chúng tôi đang làm việc với Bitmap kích thước lớn. cho rằng tôi có một giải pháp, với giải mã hình ảnh chúng ta có thể dễ dàng tránh được lỗi này.

BitmapFactory.Options o = new BitmapFactory.Options(); 
o.inJustDecodeBounds = true; 
FileInputStream fis = new FileInputStream(files.getAbsolutePath()); 
BitmapFactory.decodeStream(fis, null, o); 
fis.close(); 
final int REQUIRED_SIZE=70; 
int width_tmp=o.outWidth, height_tmp=o.outHeight; 
int scale=1; 
while(true){ 
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) 
break; 
width_tmp/=2; 
height_tmp/=2; 
scale*=2; 
} 
BitmapFactory.Options op = new BitmapFactory.Options(); 
op.inSampleSize = scale; 
fis = new FileInputStream(files.getAbsolutePath()); 
bitmap[i] = BitmapFactory.decodeStream(fis, null, op); 
fis.close(); 
+1

Vì vậy, bạn đang rescaling hình ảnh khi bạn nhận được một OOM? Tôi đoán đó là một giải pháp tiềm năng, nhưng nó không trả lời câu hỏi ban đầu của tôi. – EightyEight

0

Hệ thống không điều chỉnh kích thước heap. Ứng dụng của bạn có không gian heap được xác định trước (bán). Kích thước heap có thể được điều chỉnh trong một số ROM tùy chỉnh. Tuy nhiên, đây không phải là vấn đề thực sự.

Thứ tự của bộ sưu tập rác mà bạn thấy dường như cho thấy rằng tại thời điểm GC_BEFORE_OOM xảy ra, ứng dụng của bạn đang hết dung lượng lưu trữ. Các số liệu thống kê được báo cáo bởi trình ghi nhật ký Dalvik có thể là toàn hệ thống.

+1

Số trong dấu ngoặc đơn là ID tiến trình, vì vậy số liệu thống kê được báo cáo là cho mỗi quy trình, không phải hệ thống rộng. Tôi đang cố gắng giải quyết sự không phù hợp này. Dalvik báo cáo 30% bộ nhớ có sẵn và dòng tiếp theo cho biết bộ nhớ đã hết. Kỳ lạ. – EightyEight

Các vấn đề liên quan