Fresco: Fresco lib๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋™์•ˆ ๋นˆ๋ฒˆํ•œ OOM

์— ๋งŒ๋“  2017๋…„ 03์›” 15์ผ  ยท  15์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/fresco

์„ค๋ช…

์šฐ๋ฆฌ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ชจ๋“  Bitmaps์— ๋Œ€ํ•ด gradle( 1.1.0 )์„ ํ†ตํ•ด fresco lib๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ๋™์•ˆ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์šฉํ•œ ํ›„ ์ž์ฃผ OOM์„ ๋ฐœ์ƒ์‹œํ‚ค๊ณ , ์šฐ๋ฆฌ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์€ ํ•œ ๋ฒˆ์— ๋งŽ์€ ์ด๋ฏธ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. 8- 10๊ฐœ์˜ ์ด๋ฏธ์ง€(๋ชจ๋‘ ๊ฐ™์€ ํฌ๊ธฐ๋Š” ์•„๋‹ˆ๋ฉฐ ์ผ๋ถ€๋Š” ํ™”๋ฉด์˜ ์ ˆ๋ฐ˜์„ ๋ฎ๊ณ  ์ผ๋ถ€๋Š” 50-70dp์˜ ํฌ๊ธฐ๋งŒ ์ฐจ์ง€ํ•จ)๊ฐ€ ๋‹จ์ผ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๊ณ  ๋ชจ๋‘ ํ”„๋ ˆ์Šค์ฝ”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋กœ๋“œ๋˜๋ฉฐ,

์‚ฌ์šฉ๋˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์ด๋ฏธ์ง€๋Š” ListView, GridView, RecyclerView์™€ ๊ฐ™์€ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ๊ตฌ์„ฑ ์š”์†Œ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ˜๋ฉด ์ผ๋ถ€ ์ด๋ฏธ์ง€๋Š” ์ž์ฒด ๋กœ์ง์„ ํ†ตํ•ด ์žฌํ™œ์šฉ๋˜๋ฉฐ ์•„๋ž˜๋Š” ๋™์ผํ•œ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค.

๋ฉ”๋ชจ:

  1. ํ”„๋ ˆ์Šค์ฝ”๋Š” setDownsampleEnabled ์‚ฌ์šฉํ•˜์—ฌ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.
  2. ๋กœ๋“œ๋˜๋Š” ๋ชจ๋“  ์ด๋ฏธ์ง€๋Š” resized ์ด์ง€๋งŒ ์ผ๋ถ€ ์ด๋ฏธ์ง€๋Š” .png ์œ ํ˜•์ด์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์€ jpeg ํ˜•์‹์ž…๋‹ˆ๋‹ค.

์ƒ์‹

์ถฉ๋Œ ๋กœ๊ทธ

03-15 22:21:21.199 7035-7203/com.xyz E/AndroidRuntime: FATAL EXCEPTION: Thread-442
                                                          Process: com.xyz, PID: 7035
                                                          java.lang.OutOfMemoryError: Failed to allocate a 1638412 byte allocation with 589552 free bytes and 575KB until OOM
                                                              at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
                                                              at android.graphics.Bitmap.nativeCreate(Native Method)
                                                              at android.graphics.Bitmap.createBitmap(Bitmap.java:831)
                                                              at android.graphics.Bitmap.createBitmap(Bitmap.java:808)
                                                              at android.graphics.Bitmap.createBitmap(Bitmap.java:775)
                                                              at com.facebook.imagepipeline.memory.BitmapPool.alloc(BitmapPool.java:55)
                                                              at com.facebook.imagepipeline.memory.BitmapPool.alloc(BitmapPool.java:30)
                                                              at com.facebook.imagepipeline.memory.BasePool.get(BasePool.java:259)
                                                              at com.facebook.imagepipeline.platform.ArtDecoder.decodeStaticImageFromStream(ArtDecoder.java:137)
                                                              at com.facebook.imagepipeline.platform.ArtDecoder.decodeJPEGFromEncodedImage(ArtDecoder.java:120)
                                                              at com.facebook.imagepipeline.decoder.DefaultImageDecoder.decodeJpeg(DefaultImageDecoder.java:183)
                                                              at com.facebook.imagepipeline.decoder.DefaultImageDecoder$1.decode(DefaultImageDecoder.java:63)
                                                              at com.facebook.imagepipeline.decoder.DefaultImageDecoder.decode(DefaultImageDecoder.java:123)
                                                              at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.doDecode(DecodeProducer.java:239)
                                                              at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.access$200(DecodeProducer.java:111)
                                                              at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder$1.run(DecodeProducer.java:144)
                                                              at com.facebook.imagepipeline.producers.JobScheduler.doJob(JobScheduler.java:207)
                                                              at com.facebook.imagepipeline.producers.JobScheduler.access$000(JobScheduler.java:27)
                                                              at com.facebook.imagepipeline.producers.JobScheduler$1.run(JobScheduler.java:78)
                                                              at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
                                                              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
                                                              at com.facebook.imagepipeline.core.PriorityThreadFactory$1.run(PriorityThreadFactory.java:43)
                                                              at java.lang.Thread.run(Thread.java:818)
03-15 22:44:53.849 8055-8055/com.xyz E/AndroidRuntime: FATAL EXCEPTION: main
                                                          Process: com.xyz, PID: 8055
                                                          android.view.InflateException: Binary XML file line #2: Binary XML file line #2: Error inflating class <unknown>
                                                              at android.view.LayoutInflater.inflate(LayoutInflater.java:539)
                                                              at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
                                                              at com.xyz.view.newswipe.view.FbLikesGridView$FbLikesAdapter.onCreateViewHolder(FbLikesGridView.java:195)
                                                              at com.xyz.view.newswipe.view.FbLikesGridView$FbLikesAdapter.onCreateViewHolder(FbLikesGridView.java:160)
                                                              at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6319)
                                                              at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5507)
                                                              at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5392)
                                                              at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5388)
                                                              at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2149)
                                                              at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:556)
                                                              at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1496)
                                                              at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:593)
                                                              at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:170)
                                                              at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3535)
                                                              at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:2979)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                              at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1112)
                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:632)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                              at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1112)
                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:632)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
                                                              at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
                                                              at android.view.View.measure(View.java:18788)
                                                              at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
                                                              at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
                                                              at android.view.View.measure(View.java:18788)
                                                            at android.view.ViewGroup.measureChildWithMargins(ViewG


[FILL THIS OUT: ์–ด๋–ป๊ฒŒ ๋ฒ„๊ทธ๋ฅผ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๊ฐ€๋Šฅํ•˜๋ฉด ๊ด€๋ จ ์ด๋ฏธ์ง€ ๋˜๋Š” ์ƒ˜ํ”Œ ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ URL์„ ์ œ๊ณตํ•˜์‹ญ์‹œ์˜ค.]

ํ•ด๊ฒฐ์ฑ…

[์„ ํƒ ์‚ฌํ•ญ: ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฌด์—‡์„ ํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์ด์ƒ์ ์œผ๋กœ๋Š” ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•˜์„ธ์š”.]

์ถ”๊ฐ€ ์ •๋ณด

  • ํ”„๋ ˆ์Šค์ฝ” ๋ฒ„์ „: [1.1.0]
  • ํ”Œ๋žซํผ ๋ฒ„์ „: [ Android ๋ฒ„์ „ 5 ์ด์ƒ]

์ด๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ํ”„๋ ˆ์Šค์ฝ”๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

        ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
                .newBuilder(getApplicationContext())
                .setDownsampleEnabled(true)
                .setBitmapMemoryCacheParamsSupplier(new FrescoCacheParams(activityManager))
                .build();

        Fresco.initialize(getApplicationContext(), imagePipelineConfig);

์ด๊ฒƒ์ด FrescoCacheParams ํด๋ž˜์Šค๊ฐ€ ์ •์˜๋˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

public class FrescoCacheParams implements Supplier<MemoryCacheParams> {


    private ActivityManager activityManager;

    public FrescoCacheParams(ActivityManager activityManager) {
        this.activityManager = activityManager;
    }

    <strong i="17">@Override</strong>
    public MemoryCacheParams get() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

            int cacheSize = getMaxCacheSize();
            Log.d("####","fresco cache size = " + cacheSize);

            return new MemoryCacheParams(cacheSize, 1, 1, 1, 1);
        } else {
            return new MemoryCacheParams(
                    getMaxCacheSize(),
                    256,
                    Integer.MAX_VALUE,
                    Integer.MAX_VALUE,
                    Integer.MAX_VALUE);
        }
    }

    private int getMaxCacheSize() {
        final int maxMemory = Math.min(activityManager.getMemoryClass()
                * ByteConstants.MB, Integer.MAX_VALUE);

        if (maxMemory < 32 * ByteConstants.MB) {
            return 4 * ByteConstants.MB;
        } else if (maxMemory < 64 * ByteConstants.MB) {
            return 6 * ByteConstants.MB;
        } else {

            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD) {
                return 8 * ByteConstants.MB;
            } else {
                return maxMemory / 6;
            }
        }
    }
}

์ด๊ฒƒ์ด ๋ชจ๋“  ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ๋˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ width ๋ฐ height ๋Š” ๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ํ‘œ์‹œํ•  ๋ทฐ์˜ ์‹ค์ œ ํฌ๊ธฐ์ž…๋‹ˆ๋‹ค.

            ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                    .setResizeOptions(new ResizeOptions(width, height))
                    .build();
            DraweeController controller = Fresco.newDraweeControllerBuilder()
                    .setOldController(getController())
                    .setImageRequest(request)
                    .build();
            setController(controller);

์•„๋ž˜๋Š” AdapterView ์˜ ์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌํ˜„์ด ์žˆ์œผ๋ฏ€๋กœ ์žฌํ™œ์šฉ ๋กœ์ง์˜ ์Šค๋‹ˆํŽซ์ž…๋‹ˆ๋‹ค. ๋ฆฌ์†Œ์Šค ํ•ด์ œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

       // calling on each Drawee, once it moves out of visible area
        if (getController() != null) {
            getController().onDetach();
        }

````

   // once a image moves out of display area, we are evicting it out of pipeline as well.
    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    Uri uri = Uri.parse(url);
    imagePipeline.evictFromMemoryCache(uri);

```

์šฐ๋ฆฌ๋Š” ๋ชจ๋‘์— ์œ„์˜ ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” Drawee's ๊ทธ๋“ค์ด ์šฐ๋ฆฌ์— ๋ณผ ์ˆ˜์žˆ๋Š” ์˜์—ญ ๋ฐ–์œผ๋กœ ์ด๋™ํ•˜๋ฉด, ์นด๋“œ์—์„œ ๋‚ด ์ด๋ฏธ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” AdapterView

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@nucleartip ์žฌ๋ฏธ์žˆ๋Š” ์‚ฌ์‹ค์€ OOM์˜ Glide๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์—„์ฒญ๋‚œ ์–‘์„ ๊ฐ์•ˆํ•  ๋•Œ Glide์—์„œ Fresco๋กœ ๋ชจ๋“  ๊ฒƒ์„ ํฌํŒ…ํ•˜๋ฉด์„œ ์™”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค...

๋ชจ๋“  15 ๋Œ“๊ธ€

์•ˆ๋…•ํ•˜์„ธ์š” @nucleartip , ๋ฒ„๊ทธ ๋ณด๊ณ ์„œ๋ฅผ ์ œ์ถœํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

OOM์ด ๋ฐœ์ƒํ•˜๊ธฐ ์ „์— ๋ฉ”๋ชจ๋ฆฌ ๋คํ”„๋ฅผ ์บก์ฒ˜ํ•˜์—ฌ ๋ฌด์—‡์ด ๋ชจ๋“  ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์…จ์Šต๋‹ˆ๊นŒ?

๊ทธ๊ฑด ๊ทธ๋ ‡๊ณ , Lollipop ์ด์ƒ์˜ ๊ฒฝ์šฐ maxCacheEntrySize=1์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ๋ฅผ ์™„์ „ํžˆ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ ค๋Š” ์‹œ๋„์ž…๋‹ˆ๊นŒ ์•„๋‹ˆ๋ฉด ๋‹จ์ผ ํ•ญ๋ชฉ์œผ๋กœ ์ œํ•œํ•˜๋ ค๋Š” ์‹œ๋„์ž…๋‹ˆ๊นŒ?

@erikandre ํฐ ๋Œ€๊ธฐ์—ด ํฌ๊ธฐ๋กœ

๋คํ”„๋ฅผ ์ œ๊ณตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด OOM์ด Samsung S7, ์ตœ์‹  Nexus ์‹œ๋ฆฌ์ฆˆ ์ „ํ™” ๋“ฑ๊ณผ ๊ฐ™์€ ์ตœ์‹  ์„ธ๋Œ€ ์žฅ์น˜์—์„œ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ ์ด์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ œ์•ˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@erikandre ๋Š” OOM์œผ๋กœ ์ธํ•ด ์•ฑ์ด ์ถฉ๋Œํ•˜๊ธฐ ์ง์ „์— ์ฐ์€ ๋ฉ”๋ชจ๋ฆฌ ๋คํ”„์˜ ์Šค๋ƒ…์ƒท์ž…๋‹ˆ๋‹ค.

screen shot 2017-03-16 at 12 02 24 pm

@nucleartip , ๊ทธ ๋ฉ”๋ชจ๋ฆฌ ๋คํ”„๋ฅผ ๋‚˜์™€ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@erikandre ํž™ ๋คํ”„ ํฌ๊ธฐ๋Š” ์•ฝ

@nucleartip ๋‘˜ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค :)

์•„์ง ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜๋‚˜์š”? ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Eclipse Memory Analyzer๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋คํ”„๋ฅผ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
์ €๋„ byte[]๋ฅผ ๋„ˆ๋ฌด ๋งŽ์ด ๋ดค์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ GC ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฐธ์กฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๊ทธ๊ฒƒ๋“ค์€ NativePooledByteBuffer ๋˜๋Š” NativeMemoryChunk์™€ ๊ฐ™์€ ๋„ค์ดํ‹ฐ๋ธŒ์—์„œ ์ฐธ์กฐ๋˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๋‚˜๋Š” ๊ทธ๊ฒƒ๋“ค์ด com.facebook.common.references.SharedReference.sLiveObjects์— ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ ์ฆ๊ฑฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์™œ ์ถœ์‹œ๊ฐ€ ์•ˆ๋˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ๋„ค์š”...
์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์˜ˆ์™ธ ์ •๋ณด์ž…๋‹ˆ๋‹ค.

E/AndroidRuntime: FATAL EXCEPTION: Thread-5458 Process:
    package-name, PID: 4877
    java.lang.OutOfMemoryError: Failed to allocate a 2560012 byte allocation with 1782384 free bytes and 1740KB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.Bitmap.nativeCreate(Native Method)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:843)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:820)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:787)
    at com.facebook.imagepipeline.memory.BitmapPool.alloc(BitmapPool.java:55)
    at com.facebook.imagepipeline.memory.BitmapPool.alloc(BitmapPool.java:30)
    at com.facebook.imagepipeline.memory.BasePool.get(BasePool.java:260)
    at com.facebook.imagepipeline.platform.ArtDecoder.decodeStaticImageFromStream(ArtDecoder.java:137)
    at com.facebook.imagepipeline.platform.ArtDecoder.decodeJPEGFromEncodedImage(ArtDecoder.java:120)
    at com.facebook.imagepipeline.decoder.DefaultImageDecoder.decodeJpeg(DefaultImageDecoder.java:186)
    at com.facebook.imagepipeline.decoder.DefaultImageDecoder$1.decode(DefaultImageDecoder.java:63)
    at com.facebook.imagepipeline.decoder.DefaultImageDecoder.decode(DefaultImageDecoder.java:126)
    at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.doDecode(DecodeProducer.java:240)
    at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder.access$200(DecodeProducer.java:112)
    at com.facebook.imagepipeline.producers.DecodeProducer$ProgressiveDecoder$1.run(DecodeProducer.java:145)
    at com.facebook.imagepipeline.producers.JobScheduler.doJob(JobScheduler.java:207)
    at com.facebook.imagepipeline.producers.JobScheduler.access$000(JobScheduler.java:27)
    at com.facebook.imagepipeline.producers.JobScheduler$1.run(JobScheduler.java:78)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    at com.facebook.imagepipeline.core.PriorityThreadFactory$1.run(PriorityThreadFactory.java:43)
    at java.lang.Thread.run(Thread.java:818)

์ด๊ฒƒ์€ OOM์ด ๋ฐœ์ƒํ•˜๊ธฐ ์ „์˜ ๋ชจ๋“  GC ์ •๋ณด์ž…๋‹ˆ๋‹ค.

I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
I/art: Alloc sticky concurrent mark sweep GC freed 92215(9MB) AllocSpace objects, 36(660KB) LOS objects, 8% free, 117MB/128MB, paused 1.228ms total 37.123ms
I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 188MB to 128MB
I/art: Alloc partial concurrent mark sweep GC freed 45395(2MB) AllocSpace objects, 43(860KB) LOS objects, 3% free, 124MB/128MB, paused 727us total 59.648ms
I/art: WaitForGcToComplete blocked for 25.090ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
I/art: Starting a blocking GC Alloc
W/art: Suspending all threads took: 5.155ms
I/art: Waiting for a blocking GC Alloc
I/art: Alloc sticky concurrent mark sweep GC freed 2803(277KB) AllocSpace objects, 2(40KB) LOS objects, 0% free, 126MB/128MB, paused 579us total 19.249ms
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Alloc sticky concurrent mark sweep GC freed 822(87KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 126MB/128MB, paused 547us total 12.316ms
I/art: WaitForGcToComplete blocked for 11.410ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 190MB to 128MB
I/art: Alloc partial concurrent mark sweep GC freed 4725(241KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 126MB/128MB, paused 658us total 46.759ms
I/art: WaitForGcToComplete blocked for 46.699ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 190MB to 128MB
I/art: Alloc concurrent mark sweep GC freed 801(78KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 126MB/128MB, paused 796us total 40.098ms
I/art: WaitForGcToComplete blocked for 35.773ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Forcing collection of SoftReferences for 2MB allocation
I/art: Waiting for a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
I/art: Clamp target GC heap from 191MB to 128MB
I/art: Alloc concurrent mark sweep GC freed 3661(366KB) AllocSpace objects, 1(20KB) LOS objects, 0% free, 127MB/128MB, paused 634us total 61.758ms
I/art: WaitForGcToComplete blocked for 60.363ms for cause Alloc
I/art: Starting a blocking GC Alloc
I/art: Waiting for a blocking GC Alloc
W/art: Suspending all threads took: 38.816ms
/I/art: Forcing collection of SoftReferences for 2MB allocation
/I/art: Waiting for a blocking GC Alloc
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 214(8KB) AllocSpace objects, 1(416KB) LOS objects, 0% free, 126MB/128MB, paused 876us total 42.655ms
/I/art: WaitForGcToComplete blocked for 82.057ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Starting a blocking GC Alloc
/W/art: Throwing OutOfMemoryError "Failed to allocate a 2560012 byte allocation with 1643888 free bytes and 1605KB until OOM"
/I/art: Waiting for a blocking GC Alloc
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 12633(1496KB) AllocSpace objects, 0(0B) LOS objects, 1% free, 126MB/128MB, paused 684us total 43.271ms
/I/art: WaitForGcToComplete blocked for 136.549ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Forcing collection of SoftReferences for 2MB allocation
/I/art: Waiting for a blocking GC Alloc
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 2239(265KB) AllocSpace objects, 0(0B) LOS objects, 1% free, 126MB/128MB, paused 1.282ms total 40.182ms
/I/art: WaitForGcToComplete blocked for 120.837ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/I/art: Forcing collection of SoftReferences for 2MB allocation
/I/art: Waiting for a blocking GC Alloc
/W/art: Suspending all threads took: 30.572ms
/I/art: Clamp target GC heap from 190MB to 128MB
/I/art: Alloc concurrent mark sweep GC freed 7(1264B) AllocSpace objects, 0(0B) LOS objects, 1% free, 126MB/128MB, paused 2.697ms total 43.668ms
/I/art: WaitForGcToComplete blocked for 129.040ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: WaitForGcToComplete blocked for 83.495ms for cause Alloc
/I/art: Starting a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc
/W/art: Throwing OutOfMemoryError "Failed to allocate a 2560012 byte allocation with 1782384 free bytes and 1740KB until OOM"
/I/art: Waiting for a blocking GC Alloc
/I/art: Waiting for a blocking GC Alloc

1 ์ถฉ๋Œํ•˜๊ธฐ ์ „์— ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€์—์„œ Viewpager + RecyclerView ํŽ˜์ด์ง€๋ฅผ ์—ฝ๋‹ˆ๋‹ค.
1

์•ฝ 20์žฅ์˜ ์‚ฌ์ง„์ด ์žˆ์Šต๋‹ˆ๋‹ค.

2 ์ถฉ๋Œํ•˜๊ธฐ ์ „์— ์ฒ˜์Œ ๋‘ ํŽ˜์ด์ง€์—์„œ Viewpager + RecyclerView ํŽ˜์ด์ง€๋ฅผ ์—ฝ๋‹ˆ๋‹ค.
2

์•ฝ 40์žฅ์˜ ์‚ฌ์ง„์ด ์žˆ์œผ๋ฉฐ, ํ˜ธ์ถœ๊ธฐ์˜ ์–‘์ชฝ์— ์žˆ๋Š” Viewpager์—๋Š” ๊ฐ๊ฐ 30์žฅ์˜ ์‚ฌ์ง„์ด ์žˆ์Šต๋‹ˆ๋‹ค.

3 ์ถฉ๋Œ ํ›„:
3

ํŠนํžˆ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ RAM์ด ๋” ๋งŽ์€ ์ตœ์‹  ์žฅ์น˜์—์„œ ์ด๋Ÿฌํ•œ ํ˜„์ƒ์ด ์ ์  ๋” ์ž์ฃผ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ๋” ๋งŽ์€ RAM Fresco๊ฐ€ OOM์„ ๋” ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‚ด ๊ตฌ์„ฑ์€ ๊ธฐ๋ณธ ๊ตฌ์„ฑ์ด์ง€๋งŒ(์•„์ง ๊ณต๊ธ‰์—…์ฒด๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜์ง€ ์•Š์Œ) ์ด๊ฒƒ์ด ๋งŽ์€ ๋„์›€์ด ๋  ๊ฒƒ์ธ์ง€ ์‹ฌ๊ฐํ•˜๊ฒŒ ์˜์‹ฌ๋ฉ๋‹ˆ๋‹ค(์ตœ๋Œ€ ์บ์‹œ ํ•ญ๋ชฉ์„ 0์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ์บ์‹œ๋ฅผ ์™„์ „ํžˆ ๋น„ํ™œ์„ฑํ™”ํ•˜์ง€ ์•Š๋Š” ํ•œ).

์šฐ๋ฆฌ๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

@mradzinski ๊ฒฐ๊ตญ Glide ๋กœ ์™„์ „ํžˆ ํฌํŒ…ํ•˜์—ฌ OOM์—์„œ ๋ฌด๋ฃŒ๋กœ ์•ฑ์„ ์„ ์–ธํ–ˆ์Šต๋‹ˆ๋‹ค.

@nucleartip ์žฌ๋ฏธ์žˆ๋Š” ์‚ฌ์‹ค์€ OOM์˜ Glide๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์—„์ฒญ๋‚œ ์–‘์„ ๊ฐ์•ˆํ•  ๋•Œ Glide์—์„œ Fresco๋กœ ๋ชจ๋“  ๊ฒƒ์„ ํฌํŒ…ํ•˜๋ฉด์„œ ์™”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค...

์กฐ์‚ฌ๋ฅผ ๊ณ„์†ํ•˜๊ธฐ์—๋Š” ์ •๋ณด๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ซ์Šต๋‹ˆ๋‹ค.
๊ธฐํƒ€ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ๋ฌธ์ œ์˜ ๊ฒฝ์šฐ ์ƒˆ ์ž‘์—…์„ ๋งŒ๋“ค๊ณ  ๊ฐ€๊ธ‰์ ์ด๋ฉด HPROF ๋ฉ”๋ชจ๋ฆฌ ๋คํ”„๋ฅผ ํฌํ•จํ•˜์‹ญ์‹œ์˜ค.

@erikandre

OOM ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์— ์ •๋ณด๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. OOM ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š” ๋Œ€์‹  ์ฝœ๋ฐฑ์œผ๋กœ ์˜ค๋ฅ˜ ์ด๋ฒคํŠธ๋ฅผ ๊ฒŒ์‹œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. OOM ์ด๋ฒคํŠธ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

@AlexCJW OOM๊ณผ ๊ฐ™์€ ์‹œ์Šคํ…œ์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•ด๋‹น ์ฝœ๋ฐฑ ์ดํ›„ OOM์„ ์ˆ˜์‹ ํ•œ ํ›„ ์•ฑ์ด ์ฝœ๋ฐฑ์— ์‘๋‹ตํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ƒ๊ฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฉ”๋ชจ๋ฆฌ๋„ ์†Œ๋น„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰