난이도 : 중급
Brian Goetz, Principal Consultant, Quiotix
2006 년 1 월 24 일
지난 달에는 약한 참조(weak references)에 대해 설명했다. 이번 달에는 또 다른 형식의 참조 객체(Reference)인 유연한 참조(soft references)를 설명한다. 이것 역시 메모리 사용을 관리하고 잠재적인 메모리 누수를 없애는 보조 가비지 컬렉터이다.
가비지 컬렉션으로 자바 프로그램은 메모리 누수에 대해 면역성을 갖는다. 적어도 좁은 의미의 "메모리 누수 "의 경우가 그렇다는 것이다. 하지만 자바 프로그램의 객체 수명주기의 문제를 완전히 무시할 수 있다는 의미는 아니다. 자바 프로그램에서의 메모리 누수는 객체의 수명주기에 대한 주의가 부족할 때나 객체 수명 주기를 관리하는 표준 방식들을 파괴했을 때 발생한다. 예를 들어, 지난 시간에는 우리는 객체의 수명주기를 분명히 정하지 못하면, 메타데이터와 일시적인 객체를 제휴하려고 할 때 의도하지 않은 객체 보유를 야기하게 된다는 것을 배웠다. 객체 수명주기 관리를 무시하거나 메모리 누수로 진행될 수 있는 다른 이디엄들도 있다.
객체 로이터링(Object loitering)
메모리 누수의 한 형태인 객체 로이터링은 Listing 1의 LeakyChecksum 클래스가 설명하고 있다. 이것은 getFileChecksum() 메소드를 제공하여 파일 컨텐트의 체크섬을 계산한다. getFileChecksum() 메소드는 파일 내용들을 버퍼로 읽어 들여 체크섬을 계산한다. 보다 단순한 구현은 버퍼를 getFileChecksum() 내의 로컬 변수로 할당하는 것이다. 인스턴스 필드에 버퍼를 캐싱하여 메모리 혼합을 줄인다. 이러한 "최적화 " 로는 부족하다. 객체 할당이 더 낫다. (또한 버퍼를 로컬 변수에서 인스턴스 변수로 승격시키면 추가적인 동기화 없이 더 이상의 쓰레드 보안 없는 클래스로 만든다. 단순한 구현은 getFileChecksum()이 synchronized로 선언될 필요가 없고, 동시에 호출될 때 더 나은 확장성을 제공한다.)
Listing 1. "object loitering" 클래스
이 클래스는 많은 문제들을 갖고 있지만 메모리 누수에 집중해 보자. 버퍼를 캐싱하려는 결정은 이것이 프로그램 내에서 여러 번 호출될 수 있고 재할당 보다는 버퍼를 재사용하는 것이 더 효율적이라는 가정에서 나온 것이다. 하지만 결과적으로 버퍼는 결코 릴리스 되지 않는다. (LeakyChecksum 객체가 가비지 컬렉션이 되지 않는 한) 프로그램을 통해 언제나 접근할 수 있기 때문이다. 더욱 나쁜 것은 이것은 줄어들 수 없기 때문에 LeakyChecksum은 영구적으로 버퍼를 보유하게 된다. 이는 가비지 컬렉터에 많은 부담을 주고 더 많은 컬렉션을 요구한다. 앞으로의 체크섬을 계산할 목적으로 큰 버퍼를 사용한다면 가장 비효율적인 일이 아닐 수 없다.
LeakyChecksum에서 문제의 원인은 버퍼가 논리적으로 getFileChecksum() 연산에만 국한되기 때문이다. 하지만 수명 주기는 인공적으로 늘어나서 인스턴스 필드까지 진입한다. 결과적으로 JVM 대신에 이 클래스가 버퍼의 수명 주기를 관리해야 한다.
유연한 참조(soft reference)
이전 글에서 객체가 프로그램에 의해 사용되는 동안 객체에 접근하는 대안 방식을 약한 참조(weak references)가 제공하는 방법을 배웠다. 하지만 수명은 연장하지 않았다. Reference의 또 다른 하위 클래스인 유연한 참조(soft reference)는 다르면서도 관련된 목표를 수행한다. 약한 참조의 경우 애플리케이션이 가비지 컬렉션을 방해하지 않는 참조를 만들도록 한 반면 유연한 참조에서는 애플리케이션이 몇몇 객체를 "소모 가능한 것 "으로 위임하여 보조 가비지 컬렉터를 모으도록 하고 있다. 애플리케이션이 메모리를 사용하는지의 여부를 파악하는데 가비지 컬렉터가 제 기능을 하기 때문에 가용 메모리의 적절한 사용법을 결정하는 것은 애플리케이션에 달려있다. 애플리케이션이 객체 보유에 대해 그릇된 결정을 내린다면 퍼포먼스가 나빠진다. 왜냐하면 가비지 컬렉터는 애플리케이션이 메모리를 초과하여 실행되는 것을 막아야 하기 때문이다.
캐싱은 일반적인 퍼포먼스 최적화 방법이다. 애플리케이션이 이전 계산의 결과를 재사용 하는 것이 가능하다. 다시 계산을 할 필요가 없다. 캐싱은 CPU 활용과 메모리 사용은 서로 상쇄된다. 이상적인 균형은 얼마나 많은 메모리를 사용할 수 있는가에 달려있다. 너무 적은 캐싱으로는 원하는 퍼포먼스를 얻을 수 없다. 또한, 캐싱이 너무 많으면 퍼포먼스가 타격을 받는다. 너무 많은 메모리가 캐싱에 소비되기 때문에 다른 것에 쓰일 수 있는 것이 충분치 않기 때문이다. 메모리 수요를 결정할 때에는 애플리케이션 보다 가비지 컬렉터가 더 나은 위치에 있기 때문에 이러한 결정을 내릴 때에는 가비지 컬렉터의 도움을 받는 것이 합리적이다. 바로 이것이 유연한 참조가 하는 일이다.
객체에 유일하게 남아있는 참조가 약한 참조이거나 유연한 참조라면 그 객체는 유연하게 접근할 수 있다. 가비지 컬렉터는, 약하게 접근 할 수 있는 객체에 수행하는 것 처럼 이러한 객체들을 공격적으로 모으지 않는다. 대신 메모리가 정말 필요할 경우에만 유연하게 접근할 수 있는 객체를 모은다. 유연한 참조는 가비지 컬렉터에게 "메모리가 그렇게 적지 않다면 이 객체를 보유하고 싶다. 하지만 메모리가 부족하면 계속 진행하여 메모리를 모으면 처리하겠다. "라는 명령하는 방식이라고 할 수 있다. 가비지 컬렉터는 OutOfMemoryError를 던지기 전에 모든 유연한 참조를 제거해야 한다.
캐싱된 버퍼를 관리하는 유연한 참조를 사용하여 LeakyChecksum의 문제를 픽스할 수 있다.(Listing 2). 이제 메모리가 요구되지 않는 한 버퍼가 보유 되지만, 가비지 컬렉터에 의해 사용될 수 있다.
Listing 2. 유연한 참조로 LeakyChecksum 해결하기
캐시
CachingChecksum은 유연한 참조를 사용하여 한 개의 객체를 캐싱하고 JVM이 상세를 핸들하도록 하였다. 이와 비슷하게, GUI 애플리케이션에서 비트맵 그래픽을 캐싱하는데 유연한 참조가 사용된다. 유연한 참조가 사용될 수 있는지의 열쇠는 애플리케이션이 캐싱되는 데이터의 손실에서 회복될 수 있는지에 달려있다.
한 개 이상의 객체를 캐싱해야 한다면 Map을 사용하면 된다. 하지만 유연한 참조를 적용하는 방식에는 선택권이 있다. 캐싱을 Map> 또는 SoftReference
Brian Goetz, Principal Consultant, Quiotix
2006 년 1 월 24 일
지난 달에는 약한 참조(weak references)에 대해 설명했다. 이번 달에는 또 다른 형식의 참조 객체(Reference)인 유연한 참조(soft references)를 설명한다. 이것 역시 메모리 사용을 관리하고 잠재적인 메모리 누수를 없애는 보조 가비지 컬렉터이다.
가비지 컬렉션으로 자바 프로그램은 메모리 누수에 대해 면역성을 갖는다. 적어도 좁은 의미의 "메모리 누수 "의 경우가 그렇다는 것이다. 하지만 자바 프로그램의 객체 수명주기의 문제를 완전히 무시할 수 있다는 의미는 아니다. 자바 프로그램에서의 메모리 누수는 객체의 수명주기에 대한 주의가 부족할 때나 객체 수명 주기를 관리하는 표준 방식들을 파괴했을 때 발생한다. 예를 들어, 지난 시간에는 우리는 객체의 수명주기를 분명히 정하지 못하면, 메타데이터와 일시적인 객체를 제휴하려고 할 때 의도하지 않은 객체 보유를 야기하게 된다는 것을 배웠다. 객체 수명주기 관리를 무시하거나 메모리 누수로 진행될 수 있는 다른 이디엄들도 있다.
객체 로이터링(Object loitering)
메모리 누수의 한 형태인 객체 로이터링은 Listing 1의 LeakyChecksum 클래스가 설명하고 있다. 이것은 getFileChecksum() 메소드를 제공하여 파일 컨텐트의 체크섬을 계산한다. getFileChecksum() 메소드는 파일 내용들을 버퍼로 읽어 들여 체크섬을 계산한다. 보다 단순한 구현은 버퍼를 getFileChecksum() 내의 로컬 변수로 할당하는 것이다. 인스턴스 필드에 버퍼를 캐싱하여 메모리 혼합을 줄인다. 이러한 "최적화 " 로는 부족하다. 객체 할당이 더 낫다. (또한 버퍼를 로컬 변수에서 인스턴스 변수로 승격시키면 추가적인 동기화 없이 더 이상의 쓰레드 보안 없는 클래스로 만든다. 단순한 구현은 getFileChecksum()이 synchronized로 선언될 필요가 없고, 동시에 호출될 때 더 나은 확장성을 제공한다.)
Listing 1. "object loitering" 클래스
이 클래스는 많은 문제들을 갖고 있지만 메모리 누수에 집중해 보자. 버퍼를 캐싱하려는 결정은 이것이 프로그램 내에서 여러 번 호출될 수 있고 재할당 보다는 버퍼를 재사용하는 것이 더 효율적이라는 가정에서 나온 것이다. 하지만 결과적으로 버퍼는 결코 릴리스 되지 않는다. (LeakyChecksum 객체가 가비지 컬렉션이 되지 않는 한) 프로그램을 통해 언제나 접근할 수 있기 때문이다. 더욱 나쁜 것은 이것은 줄어들 수 없기 때문에 LeakyChecksum은 영구적으로 버퍼를 보유하게 된다. 이는 가비지 컬렉터에 많은 부담을 주고 더 많은 컬렉션을 요구한다. 앞으로의 체크섬을 계산할 목적으로 큰 버퍼를 사용한다면 가장 비효율적인 일이 아닐 수 없다.
LeakyChecksum에서 문제의 원인은 버퍼가 논리적으로 getFileChecksum() 연산에만 국한되기 때문이다. 하지만 수명 주기는 인공적으로 늘어나서 인스턴스 필드까지 진입한다. 결과적으로 JVM 대신에 이 클래스가 버퍼의 수명 주기를 관리해야 한다.
유연한 참조(soft reference)
이전 글에서 객체가 프로그램에 의해 사용되는 동안 객체에 접근하는 대안 방식을 약한 참조(weak references)가 제공하는 방법을 배웠다. 하지만 수명은 연장하지 않았다. Reference의 또 다른 하위 클래스인 유연한 참조(soft reference)는 다르면서도 관련된 목표를 수행한다. 약한 참조의 경우 애플리케이션이 가비지 컬렉션을 방해하지 않는 참조를 만들도록 한 반면 유연한 참조에서는 애플리케이션이 몇몇 객체를 "소모 가능한 것 "으로 위임하여 보조 가비지 컬렉터를 모으도록 하고 있다. 애플리케이션이 메모리를 사용하는지의 여부를 파악하는데 가비지 컬렉터가 제 기능을 하기 때문에 가용 메모리의 적절한 사용법을 결정하는 것은 애플리케이션에 달려있다. 애플리케이션이 객체 보유에 대해 그릇된 결정을 내린다면 퍼포먼스가 나빠진다. 왜냐하면 가비지 컬렉터는 애플리케이션이 메모리를 초과하여 실행되는 것을 막아야 하기 때문이다.
캐싱은 일반적인 퍼포먼스 최적화 방법이다. 애플리케이션이 이전 계산의 결과를 재사용 하는 것이 가능하다. 다시 계산을 할 필요가 없다. 캐싱은 CPU 활용과 메모리 사용은 서로 상쇄된다. 이상적인 균형은 얼마나 많은 메모리를 사용할 수 있는가에 달려있다. 너무 적은 캐싱으로는 원하는 퍼포먼스를 얻을 수 없다. 또한, 캐싱이 너무 많으면 퍼포먼스가 타격을 받는다. 너무 많은 메모리가 캐싱에 소비되기 때문에 다른 것에 쓰일 수 있는 것이 충분치 않기 때문이다. 메모리 수요를 결정할 때에는 애플리케이션 보다 가비지 컬렉터가 더 나은 위치에 있기 때문에 이러한 결정을 내릴 때에는 가비지 컬렉터의 도움을 받는 것이 합리적이다. 바로 이것이 유연한 참조가 하는 일이다.
객체에 유일하게 남아있는 참조가 약한 참조이거나 유연한 참조라면 그 객체는 유연하게 접근할 수 있다. 가비지 컬렉터는, 약하게 접근 할 수 있는 객체에 수행하는 것 처럼 이러한 객체들을 공격적으로 모으지 않는다. 대신 메모리가 정말 필요할 경우에만 유연하게 접근할 수 있는 객체를 모은다. 유연한 참조는 가비지 컬렉터에게 "메모리가 그렇게 적지 않다면 이 객체를 보유하고 싶다. 하지만 메모리가 부족하면 계속 진행하여 메모리를 모으면 처리하겠다. "라는 명령하는 방식이라고 할 수 있다. 가비지 컬렉터는 OutOfMemoryError를 던지기 전에 모든 유연한 참조를 제거해야 한다.
캐싱된 버퍼를 관리하는 유연한 참조를 사용하여 LeakyChecksum의 문제를 픽스할 수 있다.(Listing 2). 이제 메모리가 요구되지 않는 한 버퍼가 보유 되지만, 가비지 컬렉터에 의해 사용될 수 있다.
Listing 2. 유연한 참조로 LeakyChecksum 해결하기
캐시
CachingChecksum은 유연한 참조를 사용하여 한 개의 객체를 캐싱하고 JVM이 상세를 핸들하도록 하였다. 이와 비슷하게, GUI 애플리케이션에서 비트맵 그래픽을 캐싱하는데 유연한 참조가 사용된다. 유연한 참조가 사용될 수 있는지의 열쇠는 애플리케이션이 캐싱되는 데이터의 손실에서 회복될 수 있는지에 달려있다.
한 개 이상의 객체를 캐싱해야 한다면 Map을 사용하면 된다. 하지만 유연한 참조를 적용하는 방식에는 선택권이 있다. 캐싱을 Map

수안이의 컴퓨터 연구실



Leave your greetings.