본문 바로가기

FrontEnd 마이닝/Android

[안드로이드] 안드로이드 메모리 관리 (Weak Reference와 Soft Reference)


이 글은 PC 버전 TISTORY에 최적화 되어있습니다.


서론

 오늘은 자바와 안드로이드에서 사용할 수 있는 Reference Object 에 대해서 설명해보도록 하겠습니다. 


Reachability

 GC(Garbage collector)는 reference의 강약에 따라 도달 가능한 객체를 제외하고 모두 쓰레기로 간주합니다. GC의 관점에서는 객체를 참조가 가능한 객체와 참조할 수 없는 객체로 봅니다. 즉, 요 놈을 내가 다시 사용할 수 있냐, 없냐를 보고 없으면 쓰레기 통으로 넣어 버리는 것이죠. 

 Reference Object는 참조의 강약에 따라 Strong Reference, Soft Reference, Weak Reference, Phantom Reference(생략) 으로 나누고 순서대로 참조가 강합니다. 하나의 객체가 Strong과 Weak 참조가 있다면 그 객체는 Strong한 객체로 생각합니다. 


Strong Reference

 Strong Reference는 new를 이용해 생성된 객체를 말합니다. 이 객체는 GC에서 무조건 제외되기 때문에 메모리 누수(Memory Leak)을 방지하기 위해서는 이 참조를 주의해서 보아야 합니다. 이 참조를 지워주지 않으면 GC에 의해 수거되지 않고 남아있기 때문에 메모리 누수를 유발할 수 있습니다.


Soft Reference

SoftReference는 다음과 같이 객체에 Reference를 넘겨줌으로서 생성할 수 있습니다. SoftReference object = new SoftReference(new Object());  이 참조는 메모리가 충분하다면 GC에 의해서 수거되지 않고, 메모리에 여유가 없다면 GC에 의해 수거됩니다.
 


Weak Reference

WeakReference는 SoftReference와 유사하지만 GC가 발생되기 전까지는 참조를 유지하고 GC가 발생하면 무조건 회수된다는 점에서 차이가 있습니다. 이는 유용하게 쓰일 수 있는데, 짧은 시간, 자주 쓰일 수 있는 객체를 이용할 때 유용하게 사용될 수 있습니다.


WeakReference wr;

public String getFileContent(String filename) {
	
	// WeakReference에 의해 파일 내용이 보존되어 있는지 체크.
	String fileContent = (wr != null) ? wr.get() : null;  
  
	if (fileContent == null) {
		// 글 내용이 비었으면 파일 이름으로 내용을 읽어와 채워준다. 
		fileContent = fileToString(filename);
		// 채워진 글 내용을 WeakReference에 저장한다.
		wr = new WeakReference(fileContent); 
	}
	
	return fileContent;
}

 위의 예시 코드를 설명하자면 GC가 발생하기 전까진 WeakReference가 글 내용을 가지고 있으면서 캐쉬 역할을 하지만 GC가 발생하면 wr.get() 은 null 을 반환하고 다시 채우는 부분을 실행함으로서 메모리 누수를 방지할 수 있는 것 입니다.



안드로이드에서의 Weak Reference

AsyncTask란? 에서 설명 드렸던 AsyncTask를 이용한 이미지를 읽어드리는 예제입니다. 안드로이드 개발자 공식 사이트에 있는 트레이닝 세션을 참고하였습니다.


class BitmapWorkerTask extends AsyncTask
{
    private final WeakReference imageViewReference;
    private int data = 0;

    public BitmapWorkerTask(ImageView imageView){
        imageViewReference = new WeakReference(imageView);
    }

    @Override
    protected Bitmap doInBackground( Integer... params){
        data = param[0];
        return decodeSampledBitmapFromResource( getResources(), data, 100, 100);
    }

    @Override
    protected void onPostExecute( Bitmap bitmap){
        if( imageViewReference != null && bitmap != null){
            final ImageViewReference imageView = imageViewReference.get();
            if( imageView != null){
                imageView.setImageBitmap( bitmap);
            }
        }
    }
}

 

 AsyncTask 라는 별도의 스레드에서 작업 중이므로 사용자는 다른 창으로 이동할 수가 있는데 ImageView의 WeakReference를 가지고 있게 하여 사용자가 다른 창으로 이동하거나 하면 작업이 끝나지 않았더라도 ImageView가 GC에 의해 제거되게 하였습니다. 그리고 onPostExecute에서 ImageView가 살아 있으면 그림을 넣게 했습니다. 하지만 이와같이 코딩하면 그리드 뷰나 리스트 뷰에서는 메모리를 아끼기 위해 스크롤 시 재활용 뷰를 사용하기 때문에 이미지 로딩이 끝났을 때 setImageBitmap 하려는 뷰가 이미 다른 이미지를 로딩하며 재활용된 뷰 일수도 있어 작업이 꼬일 수가 있습니다. 이를 위한 해결은 트레이닝 세션 을 참고하시길 바랍니다.





간단한 예를 하나 더 들자면 




WeakReferce obj = new WeakReference(new Sample()); Sample ex = obj.get(); // 필요한 작업 수행 ex = null;


이런식으로 하게 되면 new로 생성하여 Strong Reference를 지니고 WeakReference의 전달 인자로 넘겨주었기 때문에 Weak한 속성도 가지고 있으니, Strong 속성을 가져 GC에 콜렉팅 되지 않으면서 필요한 작업을 수행하고 ex = null  을 통해 Strong을 제거해 Weak해짐으로서 GC에게 메모리가 회수되게 하면 OOM(Out Of Memory)를 방지할 수 있을 것입니다.




참고사이트

  1.  http://kwongyo.tistory.com/3
  2.  https://developer.android.com/training/displaying-bitmaps/index.html
  3.  http://stackoverflow.com/questions/29053628/do-i-need-to-check-a-weakreference-inside-an-asynctask
  4.  http://darksilber.tistory.com/entry/Java-Reference-Object%EC%9D%98-%EC%9D%B4%ED%95%B4%EC%99%80-%ED%99%9C%EC%9A%A9strongweak-reference