마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Bitmap 메모리 점유에 관해서

0 추천

Bitmap 쓰다보니 모르고, 궁금한게 많이 생기네요..

이미지로딩 관련 라이브러리 쓰라고 하셔서 라이브러리 쓰기전 최대한

원인 이해를 하고 싶어 질문 드립니다.

 

아래와 같이 2가지의 소스 코드가 있습니다.

1번, 2번은 각각 독립 액티비티이고 다른 액티비티에서 호출 하는 형태입니다.

1번, 2번 액티비티는 해당 작업 완료 후 finish() 됩니다.

 

1.

Bitmap bm = Bitmap.createBitmap(1280, 1800, Bitmap.Config.ARGB_8888);

bm.compress(Bitmap.CompressFormat.JPEG, 100, out);

 

2.

Bitmap bm = Bitmap.createBitmap(1280, 1800, Bitmap.Config.ARGB_8888);

ImageView iv = new ImageView(context);

iv.setImageBitmap(bm);

 

여기서 다른 액티비티에서 1번 액티비티는 반복해서 몇번을 호출해도 문제없이

실행이 됩니다.

그치만 2번 액티비티는 반복해서 5번 이상 호출시 OOM이 발생 합니다.

 

제 짧은 지식으로 이해하자면,

2번 액티비티에서는 ImageView에서 비트맵을

참조 하고 있기 때문에 메모리에 계속 남는다

그리고 2번 액티비티를  finish()해도 바로 ImageView에서 참조중인 Bitmap의

메모리가 반환되지 않기 때문에 반복해서 호출시 OOM이 발생한다.

 

이렇게 이해 하면 되는건가요..?

그리고 이 문제는 외부 라리브러리를 사용하지 않으면 해결하기 힘든 문제 인가요..?

 

전체 코드도 추가합니다.

public class CreateFormatActivity extends ActivityHelper implements View.OnTouchListener {

    private final double A4_RATIO = 1.4151260504f;
    private final double SCREEN_RATIO = 0.65f;

    // Activity
    private LinearLayout preView = null;
    private String pdfPath = "";

    // **********************************************************************
    // Override
    // **********************************************************************

    @Override
    protected void activityStart(Bundle savedInstanceState) throws Exception {
        setContentView(R.layout.activity_format_preview);
        
        findViewById(R.id.closeBtn).setOnTouchListener(this);
        findViewById(R.id.printBtn).setOnTouchListener(this);
        preView = (LinearLayout) findViewById(R.id.previewList);

        createFormatAndPreview();
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
            switch (view.getId()) {
                case R.id.closeBtn :
                    finish();

                    break;
                case R.id.printBtn :
                    try {
                        File pdfFile = new File(pdfPath);

                        Intent intent = new Intent(Intent.ACTION_VIEW);
                        intent.setDataAndType(Uri.fromFile(pdfFile), "application/pdf");
                        startActivity(intent);
                    } catch (Exception e) {
                        //
                    }
                    break;
                default:
                    ;
            }
        }
        return false;
    }

    // **********************************************************************
    // private method
    // **********************************************************************

    private void createFormatAndPreview() {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = manager.getDefaultDisplay();
        Point theScreenResolution = new Point();
        display.getSize(theScreenResolution);

        //int basePreviewWidth = (int) (theScreenResolution.x * SCREEN_RATIO);
        //int basePreviewHeight = (int) (basePreviewWidth * A4_RATIO);

        //int basePreviewWidth = (int) A4.PORTRAIT[0];
        //int basePreviewHeight = (int) A4.PORTRAIT[1];

        int basePreviewWidth = 1280;
        int basePreviewHeight = (int) (basePreviewWidth * A4_RATIO);

        float sRatio = basePreviewWidth / A4.PORTRAIT[0];

        LogUtil.d(String.format("basePreviewWidth : %s - basePreviewHeight : %s", basePreviewWidth, basePreviewHeight));
        LogUtil.d("sRatio : " + sRatio);

        Bitmap draw_bitmap = Bitmap.createBitmap(basePreviewWidth, basePreviewHeight, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(draw_bitmap);
        canvas.drawColor(Color.WHITE);

        Paint paintFillBlack = new Paint();
        paintFillBlack.setTextSize(20f * sRatio);
        paintFillBlack.setColor(Color.BLACK);
        paintFillBlack.setStyle(Paint.Style.FILL_AND_STROKE);
        paintFillBlack.setAntiAlias(true);

        Paint paintStrokeBlack = new Paint();
        paintStrokeBlack.setTextSize(20f * sRatio);
        paintStrokeBlack.setColor(Color.BLACK);
        paintStrokeBlack.setStyle(Paint.Style.STROKE);

        /*
         create format
         */
        ArrayList<Map<String, String>> rows = new ArrayList<Map<String, String>>();
        int pageNumber = 1;
        int rowIndex = 0;
        String filePath = globals.getLocalPdfWorkDirPath();
        String fileFullPath = filePath + "DtcPrintPreview";

        while (true) {
           
            
            // 캔버스로 비트맵에 내용 그림 부분 생략  


            // 프리뷰 
            try {
                FileOutputStream out = new FileOutputStream(fileFullPath + pageNumber + ".jpg");
                draw_bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);

                int preViewWidth = theScreenResolution.x / 2;
                int preViewHeight = (int) (preViewWidth * A4_RATIO);

                ImageView imageView = new ImageView(context);
                imageView.setImageBitmap(orgImage);
                imageView.setPadding(10, 10, 10, 10);
                imageView.setScaleType(ImageView.ScaleType.FIT_XY);
                imageView.setLayoutParams(new LinearLayout.LayoutParams(preViewWidth, preViewHeight));

                preView.addView(imageView);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (rowIndex == rows.size()) {
                break;
            } else {
                ++pageNumber;
                //5회 이상 실행시 OOM발생
                draw_bitmap = Bitmap.createBitmap(basePreviewWidth, basePreviewHeight, Bitmap.Config.ARGB_8888);
                canvas = new Canvas(draw_bitmap);
                canvas.drawColor(Color.WHITE);
            }
        }

        PdfUtil pdfUtil = new PdfUtil(PrintFromDiagActivity.this);
        pdfPath = pdfUtil.createImageToPDF(filePath, "PORTRAIT", 1);
    }
}

 

다들 답변 감사합니다.

많이 배워갑니다..

익명사용자 님이 2014년 10월 31일 질문
2014년 10월 31일 수정

3개의 답변

0 추천
 
채택된 답변

개념이해

http://helloworld.naver.com/helloworld/539525

 

 

해당액티비티에 ImageView를 사용하지않는경우라면 Bitmap.recycle을하여 메모리를 늘려주는것이좋습니다

OOM이 언제발생할지모르는부분이라 예외처리가 많이필요한부분이죠..

매니페스트에largeHeap="true"와 계속적으로 예외처리+디코딩해주면서 사이즈를 줄이는방법밖에없습니다

whdrb19 (23,520 포인트) 님이 2014년 10월 31일 답변
whdrb19님이 2014년 10월 31일 reshown
일단 largeHeap="true"랑 사이즈 조금 줄여서 반복 호출 가능 횟수는 많이 늘렸는데요,, 어플 실행중 작업 관리자에서 보면 램을 엄청 먹고 있던데 largeHeap="true" 사용해도 괜찮은 옵션인가요?
0 추천
전체 코드 좀 올려주세요.

위의 부분만 가지고는 추측하기 어려울거 같은데요

원인이 다른곳에 잇을지도 모릅니다
갸아악 (21,260 포인트) 님이 2014년 10월 31일 답변
전체 소스 추가했습니다.
0 추천

안드로이드에서 이미지 관련 메모리누수는 짜증나는 이슈죠~~!

그래서 보통 흔히들 하시는 작업이 recycle이겠죠~! 하지만 이 recycle이라는 놈이 함수 호출 즉시 메모리를 확보 하는게 아니라는거죠~!

다음에 메모리 확보하때 우선적으로 확보 해달라는 선언에 불과 하다는 거죠~!

그래서 recycle을 해도 원하는 작업이 이루어 지지 않을때가 많습니다.

뭐 결론은 recycle이 일어나지 않아도 누수가 발생하지 않게끔 하는게 답이겠지요~!

썬에서도 가비지 컬렉팅에 대한 답변은 항상 잘되고 있다~! 관심꺼라~! 너희가 소스를 잘짠다면 누수는 일어 나지 않는다의 입장이니까요~!

그리고 외부라이브러리도 결국 메모리 관련 이슈는 해결 못합니다. 라이브러리도 여기에 달리는 답변의 해결책을 라이브러리화 시킨것 밖에요~!

초보자가 만들기에는 좀 어려움이 있지만만 그리 힘들기만 한 작업은 아니라 봅니다.

ThisPlus (46,920 포인트) 님이 2014년 10월 31일 답변
...