Glide 공식 문서 읽기 스터디 - 6 (28pg~33pg)
AppGlideModule
주어진 애플리케이션에는 하나의 AppGlideModule 구현만 있을 수 있다
캐시
Glide는 이미지 로딩 시 다음과 같은 순서로 캐시를 확인합니다
- 활성 리소스 (Active Resources): 현재 다른 View에서 사용 중인 이미지
- 메모리 캐시 (Memory Cache): 최근에 로드되어 메모리에 저장된 이미지
- 리소스 디스크 캐시 (Resource Disk Cache): 디코딩 및 변환된 이미지가 디스크에 저장된 경우
- 데이터 디스크 캐시 (Data Disk Cache): 원본 이미지 데이터가 디스크에 저장된 경우
모든 캐시 계층에서 이미지를 찾지 못하면, Glide는 원본 소스(예: 네트워크 URL, 로컬 파일 등)로부터 이미지를 새로 로드합니다. 즉, 캐시에 이미지가 없을 경우 원본 데이터를 다시 가져옵니다.
Glide의 기본 디스크 캐시 크기는 250MB입니다. 이 캐시는 애플리케이션의 내부 캐시 디렉토리에 위치하며, 필요에 따라 크기를 조정할 수 있습니다.
Cache Keys
Glide 4에서는 모든 캐시 키가 최소 두 가지 요소 모델(예: File, Uri, Url 등)과 시그니처를 포함합니다.
커스텀 모델을 사용할 경우에는 반드시 hashCode()와 equals()를 올바르게 구현해야 합니다.
이 외에도 Glide는 다음 정보를 조합해 캐시 키를 생성합니다
- 이미지 크기 (가로 × 세로)
- 적용된 Transformation
- 추가 옵션들
- 요청된 데이터 타입 (Bitmap, GIF 등)
활성 리소스 및 메모리 캐시에서 사용하는 키는 디코딩에 필요한 메모리 옵션에 따라, 디스크 캐시 키와 약간 다를 수 있습니다.
최종적으로 모든 요소는 해시되어 하나의 문자열 키로 변환되며, 디스크 캐시에서는 이 키가 파일 이름으로 사용됩니다.
q. 캐시 키를 커스텀해야 하는 상황이 있는지?
- 동일한 이미지가 서로 다른 URL로 제공되는 경우
- 이미지의 버전 관리가 필요한 경우
디스크 캐시 전략
DiskCacheStrategy는 Glide의 .diskCacheStrategy() 메서드로 요청별로 설정할 수 있는 디스크 캐시 전략입니다.
이 전략을 통해 디스크 캐시를 사용할지 여부와, 어떤 데이터를 저장할지 결정할 수 있습니다
- 원본 데이터만 캐시
- 변환된 이미지(썸네일 등)만 캐시
- 둘 다 캐시
- 아예 캐시하지 않음
이렇게 상황에 맞는 전략을 선택해 성능과 저장 공간을 최적화할 수 있습니다.
기본 전략인 DiskCacheStrategy.AUTOMATIC은 다음과 같은 방식으로 동작합니다
1. 원격 이미지 (예: URL)
- 저장 대상: 원본 이미지 데이터만 저장 (디코딩 전)
- 이유: 네트워크에서 다시 다운로드하는 건 비용이 크니까, 원본만 저장해서 다시 쓸 수 있게 함
- → 필요할 때마다 새로 디코딩하거나 변환함
2. 로컬 이미지 (예: 기기 내부 파일)
- 저장 대상: 변환된 이미지(썸네일 등)만 저장
- 이유: 원본은 디스크에서 쉽게 다시 가져올 수 있으므로, 굳이 저장할 필요 없음
DiskCacheStrategy를 적용하기 위해서는 다음과 같다.
Glide.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
캐시로부터만 로드하기
경우에 따라 이미지가 캐시에 없는 경우 로드가 실패 할 수 있다. 이렇게 하려면 요청별로 onlyRetrieveFromCache() 메서드를 사용 할 수 있다.
Glide.with(fragment)
.load(url)
.onlyRetrieveFromCache(true)
.into(imageView);
이미지가 메모리 캐시 또는 디스크 캐시에서 발견되면 로드된다. 그렇지 않고, 이 옵션을 true로 설정하면 로드가 실패한다.
캐시 건너뛰기
특정 요청이 디스크 캐시나 메모리 캐시 또는 둘 모두를 건너뛰도록 하기위해 Glide가 몇 가지 대안을 제공합니다. 메모리 캐시만 건너뛰려면 skipMemoryCache()를 사용합니다.
Glide.with(fragment)
.load(url)
.skipMemoryCache(true)
.into(view);
디스크 캐시만 건너뛰려면 DiskCacheStrategy.NONE을 사용합니다.
Glide.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(view);
이러한 옵션은 함께 사용될 수 있습니다.
일반적으로 캐시를 건너 뛰지 않는것이 좋습니다. 이미지를 가져오고, 디코딩 및 변환하여 새 썸네일 이미지를 만드는 것보다 캐시에서 이 미지를 로드하는 것이 훨씬 더 빠르기 때문입니다.
커스텀 캐시 무효화
Glide는 기본적으로 리소스를 캐싱할 때 고유 식별자(파일 경로, URL 등)를 사용하지만, 식별자 변경이 어려운 경우 signature() API를 통해 버전 정보를 포함시켜 캐시를 무효화할 수 있습니다.
- MediaStore 콘텐츠는 MediaStoreSignature를 사용해 수정 시간, MIME 타입, 방향 정보를 기준으로 캐시 키를 생성합니다.
- 파일은 ObjectKey(수정시간)을 통해 변경 여부를 판단할 수 있습니다.
- URL은 서버가 주소를 바꾸는 게 가장 좋지만, 불가능한 경우 버전 넘버 같은 메타데이터를 ObjectKey로 추가할 수 있습니다.
식별자나 버전 정보 추적이 불가능한 경우, 캐싱을 끄기 위해 DiskCacheStrategy.NONE을 사용할 수 있습니다.
q. 캐시 무효화라는 게 무슨 개념인지?
캐시 무효화(Cache Invalidation)는 캐시된 데이터가 더 이상 유효하지 않을 때, 이를 제거하거나 갱신하는 과정입니다.
Glide에서는 이미지가 변경됐을 때 이전 캐시를 사용하지 않도록 signature() 메서드를 활용합니다.
예를 들어, 이미지의 마지막 수정 시간을 시그니처로 넣으면 이미지가 바뀔 때마다 새로운 캐시 키가 생성되어 기존 캐시가 자동으로 무효화됩니다.
리소스 관리
Glide는 리소스 관리와 관련해 몇 가지 추가적인 설정 기능을 제공합니다.
단, 캐시 크기를 변경할 경우에는 변경 전후의 성능과 메모리 사용량을 비교해 효과적인 절충이 되는지 반드시 확인해야 합니다.
메모리 캐시
Glide는 Android 프레임워크의 ComponentCallbacks2를 통해 메모리 상태를 감지하고, 시스템 상황에 따라 메모리 캐시와 BitmapPool의 내용을 자동으로 정리합니다. 그래서 일반적으로 개발자가 직접 캐시를 모니터링하거나 수동으로 비울 필요는 없습니다.
하지만 필요할 경우 setMemoryCategory()를 통해 Glide의 메모리 사용량을 일시적으로 조절할 수 있습니다. 예를 들어 앱의 특정 화면에서 메모리를 더 적게 또는 많이 쓰도록 설정할 수 있으며, 전역 RAM 사용량 변경은 AppGlideModule 설정을 통해 가능합니다.
Glide.get(context).setMemoryCategory(MemoryCategory.LOW);
// 또는
Glide.get(context).setMemoryCategory(MemoryCategory.HIGH);
q. Low로 설정하면 어떻게 되고 High로 설정하면 어떻게 되는지?
- LOW: 메모리 캐시와 BitmapPool의 크기를 줄여 메모리 사용량을 최소화합니다. 이 설정은 메모리 사용이 제한적인 환경에서 유용합니다.
- HIGH: 메모리 캐시와 BitmapPool의 크기를 늘려 더 많은 이미지를 캐시에 저장할 수 있습니다. 이 설정은 성능을 우선시하는 환경에서 유용합니다.
메모리 정리하기
Glide의 캐시 메모리 영역과 BitmapPool을 정리하기 위해서는 간단히 clearMemory()를 호출하면 된다.
// 이 메서드는 반드시 메인 쓰레드에서 호출 되어야 한다.
Glide.get(context).clearMemory();
모든 메모리를 삭제하는 것은 특히 효율적이지 않으며 버벅거림과 로드 시간 증가를 방지하기 위해 가능한 한 피해야한다.
q. 반드시 메인 스레드에서 호출되어야 하는 이유는 무엇인지?
clearMemory() 메서드는 Glide의 메모리 캐시와 BitmapPool을 즉시 비우는 작업을 수행합니다. 이러한 작업은 UI와 관련된 리소스를 다루기 때문에 반드시 메인 스레드에서 호출되어야 합니다. 백그라운드 스레드에서 호출할 경우 UI 스레드와의 충돌로 인해 예기치 않은 동작이나 애플리케이션 크래시가 발생할 수 있습니다.
디스크 캐시
Glide는 런타임 중 디스크 캐시 크기를 일부 제어할 수 있지만, 전체 크기와 구성은 AppGlideModule에서 설정해야 합니다.
앱 전체에서 사용할 디스크 캐시 용량을 변경하려면 Glide의 Configuration 설정을 참고하세요.
또한 디스크 캐시를 비우고 싶다면 clearDiskCache() 메서드를 호출하면 됩니다.
// 이 메서드는 반드시 백그라운드에서 호출 되어야 한다.
Glide.get(applicationContext).clearDiskCache();
q. 반드시 백그라운드에서 호출해야 하는 이유는 무엇인지?
clearDiskCache() 메서드는 Glide의 디스크 캐시를 비우는 작업을 수행합니다. 이 작업은 파일 시스템에 대한 I/O 작업을 포함하므로 시간이 오래 걸릴 수 있습니다. 메인 스레드에서 이러한 작업을 수행하면 UI가 멈추거나 애플리케이션이 응답하지 않는 현상이 발생할 수 있으므로, 반드시 백그라운드 스레드에서 호출해야 합니다.
리소스 재사용
Glide는 Bitmap, byte[], int[], 그리고 다양한 POJO 객체들을 리소스로 사용합니다.
이러한 리소스는 메모리 사용을 줄이기 위해 가능한 한 재사용되며, 이는 앱의 메모리 효율성을 높이는 데 도움이 됩니다.
Benefits
크고 작은 객체를 계속 새로 생성하면 가비지 컬렉션(GC)이 자주 발생해 오버헤드가 커질 수 있습니다.
특히 Dalvik 런타임(구버전 Android)에서는 GC 패널티가 크기 때문에 성능 저하가 더 심해집니다.
하지만 최신 ART에서도 과도한 할당은 성능 저하를 일으킬 수 있으므로, Glide는 리소스 재사용을 통해 이런 문제를 줄이려 합니다.
q. Dalvik은 뭐고 ART는 뭔지?
Dalvik과 ART는 Android에서 사용하는 두 가지 런타임 환경입니다
- Dalvik: Android 4.4 (KitKat) 이하 버전에서 사용되던 런타임으로, Just-In-Time (JIT) 컴파일 방식을 사용합니다.
- ART (Android Runtime): Android 5.0 (Lollipop) 이상에서 사용되는 런타임으로, Ahead-Of-Time (AOT) 컴파일 방식을 사용하여 앱 실행 성능을 향상시킵니다.
ART는 Dalvik에 비해 메모리 관리와 가비지 컬렉션(GC) 성능이 개선되어 앱의 전반적인 성능이 향상되었습니다.
q. GC 패널티라는 건 뭔지?
GC 패널티(Garbage Collection Penalty)는 가비지 컬렉션이 실행될 때 애플리케이션의 성능이 일시적으로 저하되는 현상을 의미합니다. 특히 Dalvik 런타임에서는 GC가 실행될 때 메인 스레드가 일시적으로 중단되어 UI 렌더링이 지연되거나 프레임 드랍이 발생할 수 있습니다. 이러한 현상은 이미지 로딩이나 대용량 데이터 처리 시 더욱 두드러지게 나타납니다.
Dalvik
Dalvik(Android 5.0 이전)의 런타임 환경은 메모리 할당에 취약하며, 특히 GC(Garbage Collection) 방식에서 큰 성능 저하가 발생할 수 있습니다. Dalvik에는 두 가지 주요 GC 방식이 있습니다
- GC_CONCURRENT: 메인 스레드를 약 5ms 동안 두 번 짧게 차단하며, 이 시간은 일반적으로 1프레임(16ms)보다 짧기 때문에 UI에 큰 영향을 주지 않습니다.
- GC_FOR_ALLOC: 메모리 부족 시 발생하며, 최대 125ms 동안 메인 스레드를 중단시키는 "stop-the-world" 방식입니다. 이로 인해 여러 프레임이 드랍되고, 특히 스크롤 중인 화면에서 눈에 띄는 버벅임이 생깁니다.
문제는 Dalvik이 일반적인 크기의 메모리 할당조차도 잘 처리하지 못한다는 점입니다. 예를 들어, 16KB 버퍼 또는 Bitmap 같은 중간~대형 객체를 반복적으로 생성하면 GC_FOR_ALLOC가 자주 발생해 성능이 크게 떨어집니다.
Glide는 이러한 문제를 최소화하기 위해 중대형 자원을 가능한 한 재사용하고, stop-the-world GC 발생을 회피하는 방식으로 동작합니다. 이로써 Dalvik 환경에서도 보다 부드러운 사용자 경험을 제공할 수 있게 됩니다.
q. GC_FOR_ALLOC는 125ms동안 메인 스레드를 차단한다고 하는데 무조건 125ms까지 다 도는 것인지? 짧게 여러번 끊어서 돌리는 게 나으면 대부분 그 전략이 사용될 거라고 생각하는데 GC_FOR_ALLOC은 어떨 때 쓰이는 것인지?
아니요, 무조건 125ms를 차단하는 건 아닙니다.
여기서 125ms는 최대 차단 시간을 의미합니다. 실제 차단 시간은 상황에 따라 달라질 수 있습니다.
✔️ GC_FOR_ALLOC 요약
- GC_FOR_ALLOC은 Dalvik에서 메모리가 부족할 때 새로운 객체를 할당하려고 하면 발생하는 GC입니다.
- 이 GC는 앱의 모든 스레드(stop-the-world)를 중단시켜 가비지를 정리하고, 메모리를 확보하려고 합니다.
- 이때 메인 스레드도 중단되므로 UI가 멈추고, 프레임 드랍이 발생할 수 있어요.
✔️ 왜 그렇게 길게 멈추냐?
Dalvik은 메모리 할당 최적화가 좋지 않아서,
**조금만 큰 객체 (예: 16KB 버퍼나 Bitmap)**를 반복해서 만들면 GC_FOR_ALLOC이 자주 발생하고, 이게 프레임 드랍을 유발합니다.
✔️ GC_FOR_ALLOC은 언제 유용할까?
유용하다기보단, 마지막 수단입니다.
- 할당할 메모리가 없으면 앱이 죽을 수 있으니,
- 중단시키고서라도 메모리를 정리해서 앱을 계속 실행시키기 위해 필요한 방식입니다.
- 즉, 사용자에게 버벅거림을 주더라도 앱이 아예 죽는 것보다는 낫기 때문에 쓰는 전략이에요.
q. GC_FOR_ALLOC 말고 다른 전략을 사용하면 되는 것 아닌지?
Dalvik 런타임에서는 GC 전략이 앱 개발자가 선택하는 게 아니라 메모리 상황과 할당 패턴에 따라 Dalvik이 내부적으로 GC 전략을 정합니다.
따라서 개발자는 GC_FOR_ALLOC이 발생하지 않도록 코드를 작성해야 합니다.
🔁 객체 재사용 | Glide가 하는 것처럼 Bitmap, 배열 등을 재활용해서 새 객체 생성을 줄입니다. |
📦 Bitmap Pool 사용 | Glide의 BitmapPool 같은 풀을 써서 메모리 재사용 유도 |
🎯 불필요한 중간 객체 제거 | 예: String → Uri → File → InputStream 같은 체인을 줄이기 |
⚠️ 대형 객체 한 번에 생성하지 않기 | Bitmap이나 배열 같은 큰 객체를 반복해서 만들지 않기 |
Glide가 리소스를 추적하고 재사용하는 방식
Glide는 리소스 재사용에 대해 비교적 유연한 방식을 취합니다.
리소스가 안전하다고 판단되면 재사용하지만, Glide는 개발자에게 리소스를 명시적으로 재활용하라고 강요하지는 않습니다.
대신 Glide는 각 리소스의 참조 횟수(reference count)를 추적합니다.
이 값이 0이 되면, 해당 리소스는 더 이상 사용되지 않는 것으로 간주되어 해제되거나 Glide 내부 풀로 반환됩니다.
한 번 반환된 리소스는 재사용 대기 상태가 되므로, 그 이후에는 직접 사용하면 안 됩니다.
q. 참조 횟수를 카운트하는 이유는 뭐고, 참조 횟수가 0에 도달하면 리소스가 해제되고 재사용을 위해 Glide로 반환된다는 건 무슨 의미지인지? 리소스 해제라는 건 무슨 뜻인지?
이건 Glide 내부의 리소스 관리(=메모리 최적화) 개념입니다.
✔️ 참조 횟수(reference count)란?
- 하나의 리소스(예: 이미지 Bitmap)를 몇 개의 곳에서 동시에 사용 중인지를 나타내는 숫자예요.
- 예를 들어 하나의 이미지가 두 개의 ImageView에 보여지고 있다면, 그 이미지의 참조 횟수는 2입니다.
✔️ 왜 참조 횟수를 관리할까?
- 이미지가 모든 View에서 사라진 순간(참조 횟수 == 0) Glide는 그 이미지가 더 이상 사용되지 않는다고 판단합니다.
- 이 시점에서 Glide는 해당 리소스를 해제하거나 풀(pool)에 반환해서 나중에 재사용할 준비를 합니다.
✔️ 리소스 해제란?
- Bitmap이나 이미지 객체를 메모리에서 제거하거나 Glide의 재사용 풀로 넘기는 것입니다.
- 이걸 안 하면 계속 메모리를 차지하므로, 앱이 느려지고 메모리 부족으로 죽을 수도 있어요.
✔️ 재사용은 뭐냐?
- Glide는 자주 사용하는 리소스를 풀에 저장해뒀다가, 다음에 비슷한 요청이 들어오면 풀에서 꺼내 바로 재사용합니다.
- 새로 로딩하고 디코딩하는 비용을 아낄 수 있어서 성능이 향상됩니다.
'Android > 공식문서' 카테고리의 다른 글
Glide 공식 문서 읽기 스터디 - 5 (23pg~28pg) (0) | 2025.05.27 |
---|---|
Glide 공식 문서 읽기 스터디 - 4 (18pg~23pg) (1) | 2025.05.25 |
Glide 공식 문서 읽기 스터디 - 3 (13pg~18pg) (0) | 2025.05.18 |
Glide 공식 문서 읽기 스터디 - 2 (7pg~12pg) (0) | 2025.05.04 |
Glide 공식 문서 읽기 스터디 - 1 (~7pg) (0) | 2025.04.28 |
댓글
이 글 공유하기
다른 글
-
Glide 공식 문서 읽기 스터디 - 5 (23pg~28pg)
Glide 공식 문서 읽기 스터디 - 5 (23pg~28pg)
2025.05.27 -
Glide 공식 문서 읽기 스터디 - 4 (18pg~23pg)
Glide 공식 문서 읽기 스터디 - 4 (18pg~23pg)
2025.05.25 -
Glide 공식 문서 읽기 스터디 - 3 (13pg~18pg)
Glide 공식 문서 읽기 스터디 - 3 (13pg~18pg)
2025.05.18 -
Glide 공식 문서 읽기 스터디 - 2 (7pg~12pg)
Glide 공식 문서 읽기 스터디 - 2 (7pg~12pg)
2025.05.04