SSISO Community

시소당

Eye on performance: 프로파일링 - 툴에 다이어트가 필요할 때 어떻게 하십니까?

Eye on performance: 프로파일링

툴에 다이어트가 필요할 때 어떻게 하십니까?

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

이 페이지를 이메일로 보내기

이 페이지를 이메일로 보내기


제안 및 의견
피드백

난이도 : 초급

Jack Shirazi, Director, JavaPerformanceTuning.com
Kirk Pepperdine, CTO, JavaPerformanceTuning.com

2004 년 7 월 29 일

튜 닝은 속도에 문제가 있을 때만 하는 것은 아니다. 가끔은 애플리케이션의 다른 부분들도 조정될 필요가 있다. 애플리케이션에 튜닝이 필요하다면 가장 먼저 취하는 행동은 프로파일러로 애플리케이션을 점검하는 일이다. 그러나 프로파일링이 언제나 유용한 것은 아니다.

그동안 애플리케이션의 메모리 풋프린트를 튜닝하는 문제를 다룰 필요가 없었다. 일반적으로, 메모리와 관련된 튜닝 요구 사항들에는 가비지 컬렉션 줄이기가 포함된다. 이상적으로는 힙 사이즈와 가비지 컬렉션 알고리즘을 튜닝하여 줄인다. 그리고 실패할 경우 다양한 기술을 사용하여 객체 줄이기가 수행된다. 하지만 가끔은 할당의 효율성과 가비지 컬렉션에 관계없이 애플리케이션은 너무나 많은 메모리를 차지한다.

팻(fat) 클라이언트

최근 팻(fat) 클라이언트의 메모리 풋프린트를 줄이라는 지시를 받았다. "팻 클라이언트(fat client)"는 일반적인 GUI 클라이언트 애플리케이션을 지칭하는 것이지만 이 경우 클라이언트 거의 비만에 가까웠다. 이 클라이언트는 윈도우 플랫폼에서 실행되고 있었는데, 프로세스 사이즈를 2GB로 제한했다. 실행 가능한 주소 공간과 다양한 JNI 제품들을 포함시키는데 필요한 다른 공간들을 줄인 후에 애플리케이션에 사용 할 수 있는 최대 힙 사이즈는 1.2 에서 1.3 GB 정도가 되었다. 불행히도 이 애플리케이션의 어떤 사용자들은 데이터의 양 때문에 이 한계치에 매우 근접했다. 한가지 분명한 튜닝 옵션은 UNIX 머신으로 옮기는 것이었지만 현실적이지 않아 배제되었다. 고객은 애플리케이션의 살을 빼는 것을 더 선호하였다.

우리의 태스크가 설정되었다. 팻 클라이언트를 분석하고(profile) 모든 공간을 차지하고 있는 것이 무엇인지를 찾아낸다. 그런 다음 후기 확장이나 데이터 양의 증가에 대비해 남겨둔 사이즈까지 그 객체들을 줄인다. 생각만으로는 단순했다. 객체 감소는 오랜 시간이 걸리긴 하지만 그 크기의 힙에서 제거될 수 있는 여분의 군살이 있음에 분명했다.

일반적인 절차

일반적인 풋프린트-감소 절차를 시작했다. 테스트 환경을 설정하고 재생산 가능한 테스트를 지정하고 프로파일러를 시작하여 테스트를 실행하고 데이터를 분석하고 튜닝 기회를 찾는다. 드디어 "테스트 실행" 단계에 돌입했지만 프로파일러는 죽었다. 다시 시도했다. 그러자 다시 죽었다. 프로파일러의 설정을 바꿔 필요한 오버헤드를 최소화하고 다시 시도했다. 또 죽었다. 프로파일러의 전체 실행 또는 유용한 프로파일링 데이터를 만들어내는 JVM 힙 공간이 충분하지 않았던 것이다. 우리는 고급의 상용 프로파일러를 사용하고 있었기 때문에 더욱 놀랬다.

반복된 재시도

바다에는 물고기들이 많이 있다. 요즘 선택할 수 있는 프로파일러도 많이 있다. (참고자료) 다른 프로파일러를 사용하기로 했다. 불행히도 테스팅 결과는 거의 비슷했다. 두 번째 프로파일러는 첫 번째 프로파일러와 같은 지점에서 JVM과 충돌하였다. 첫 번째 프로파일러 처럼 설정 가능한 것이어서 오버헤드와 프로파일에서 추출한 데이터를 최소화하도록 설정했기 때문에 그나마 좀더 오래갔다. 첫 번째 프로파일러와 마찬가지였다. 충돌했다. 세 번째 프로파일러 역시 다르지 않았다.

교활한 프로파일러

네 번째 프로파일러는 교묘하게 달랐다. 활성 객체들의 스냅샷의 메모리를 분석할 때, 네 번째 프로파일러는 스냅샷이 요구되기 전까지는 JVM에 오버헤드가 전혀 없었다. 성공이다! 처음으로 우리 테스트는 프로파일러를 실행하면서, 스냅샷이 필요한 지점까지 실행되었다. 행복했다. 그리고 나서 스냅샷을 실행했을 때 JVM과 충돌했다.

다시 시도했지만 스냅샷을 만들기 위해 프로파일러가 너무나 많은 추가 프로세스 공간을 요구했다. 작동하지도 않았다. 다시 원점으로 돌아갔다. 아직도 여섯 개 정도의 상용 프로파일러가 남아있었지만 패턴은 분명했다. 생각할 시간이 필요했다.

이상하게도 문제가 되었던 것은 프로파일러의 복잡함이였다. 단순한 것이 필요했다. 물론 단순한 것이라고 해서 오버헤드가 적은 것은 아니지만 시도해볼 가치는 있었다. 왜냐하면 복잡한 프로파일러를 사용해서 너무 많은 실망을 했기 때문이다. 그래서 오픈 소스 프로파일러를 검사하기 시작했다.




위로


다시 처음으로

메모리 분석용 프로파일러로 시작했다. 첫 번째 오픈 소스 메모리 프로파일러는 매우 단순했다. 아웃풋은 매우 제한적이었다. 클래스의 리스트와 클래스별 객체 수 뿐이었다. 하지만 시작하기엔 딱 알맞았다. 이것은 곧 충돌했다. 앞서 갔던 길을 다시 가고 있다는 절망감이 들었다. 두 번째 오픈 소스 프로파일러는 첫 번째 것 보다 더 단순하였다. 하지만 실제로는 제공되는 정보가 더 상세했다. 객체 당 한 개의 엔트리를 가진 힙 덤프와 각 객체의 크기와 클래스를 보여주었다. 다른 프로파일러와 마찬가지로 작은 규모에서 시도했고 힙 덤프가 커지는 것을 볼 수 있었다. 결국 1GB의 아웃풋 파일을 보게 되었다. 시도한 결과 VM과 충돌했다. 하지만 이것은 부분적인 덤프를 주었다.

이 같이 큰 요소들을 다룰 때, 필요한 리소스 규모와 걸리는 시간 정도는 알아두어야 한다. 아웃풋을 1GB 파일로 덤핑하는데는 수분이 걸릴 수 있다. 작동이 얼마나 걸릴지 준비하지 않았다면 진행중일 때 프로세스가 멈춘 것으로 오인할 수 있다. 이 오픈 소스 프로파일러는 작동했다. 하지만 첫 번째 테스트 때 처럼 충분한 시간을 주지 않았다. 이 문제를 해결하기 위해 첫 번째의 중간에 두 번째 덤프를 주도록 했다. 충돌이 일어났다. 문제는 프로파일러가 아니고 우리라는 것을 발견했다. 다시 시도하자 작동했다.

heapprofile 프로파일러

그렇다면 어떤 프로파일러가 작동했는가? Matthias Ernst가 작성한 heapprofile 이었다. 이것은 가장 간단한 포맷으로 힙을 덤핑하기위해 Java Virtual Machine Profiler Interface (JVMPI)를 사용하는 C 코드의 페이지에 불과했다. 심지어 컴파일도 해야 한다. 사전 컴파일된 실행파일은 웹 사이트에도 없다. (참고자료) 이 문제를 위해 필요한 것은 단순함의 정도였다. 오버헤드는 전혀 없었다. 힙 또는 JNI 리소스를 최소한 사용했을 뿐이다. 프로그램이 실행되는 동안 아무것도 하지 않는다. 힙 덤프를 원했을 때 간단히 힙을 수행하면서 각 객체의 크기와 클래스를 파일에 직접 덤핑했다. 인메모리 구조의 구현은 전혀 없었다.

물론 다 끝내지는 못했다. 이제 결과 데이터를 분석하고 이를 토대로 어떤 객체들이 사용되는지를 결정해야 했다. 다행히도 아웃풋 포맷은 파싱이 쉬웠다. 문제를 일으키는 객체들을 발견하면 그러한 객체들을 위한 할당 사이트를 찾아야 했다. 오버헤드를 낮게 유지하면서 이를 수행하기 위해 그러한 몇 가지 클래스를 구조체의 스택 트래커로 재컴파일 하는 방법을 사용했다. (참고자료) 이 간단한 기술에는 구조체의 예외를 만드는 것(던지는 것이 아님)이 포함된다. 예외에는 할당 사이트에 대한 스택 트레이스가 포함된다. 그런 다음, 모든 객체들을 위해 스택의 도표를 작성한다. 대부분의 스택들이 같기 때문에 실제로 매우 많은 데이터들을 호출 스택과 각 스택으로 링크된 인스턴스의 관련 숫자를 구분해가면서 저장해야 한다.




위로


단순한 만큼 불편한

이것은 단순한 기술이긴 하지만 생산성이 높지는 않다. 데이터를 얻기 위해서 완벽한 기능의 프로파일러를 사용하는 것이 더 나았다. 우리는 처음부터 힙을 보고 많은 힙을 레퍼런싱하는 객체를 찾아낼 때까지 더 큰 섹션을 트래킹하고 싶었다. 하지만 이 같은 옵션이 없었다.

우리가 사용했던 기술은 일반적인 튜닝 임무에 비교해볼 때 엉망이였다. 비록 결과적으로는 완전히 불필요한 몇 개의 객체를 찾았지만 몇몇 클래스용 다양한 구현을 사용하여 제거할 수 있었다. 객체 감소를 사용한 케이스는 빈번하지만 팻 클라이언트를 날씬한 클라이언트로 바꾸는 솔루션은 없다. 다이어트는 자바 애플리케이션에서도 힘든 일이다. 인간의 다이어트 처럼 시간이 오래 걸린다.




위로


결론

유닉스 디스커션 그룹에서 다음과 같은 질문을 받았다. “텍스트를 편집할 때 유닉스 구루들은 무엇을 사용합니까?” vi, Emacs 등을 사용한다는 주장들이 이어졌다. 하지만 의심할 여지 없는 정확한 대답은 “유닉스 구루들은 가능한 것이라면 무엇이든 사용하여 텍스트를 편집한다.” 일 것이다. 자바 플랫폼은 정말로 훌륭한 프로파일러를 사용할 수 있는 특권을 갖고있다. 하지만 궁극적으로 튜닝을 원한다면 분석할 데이터가 필요하고 어떤 방식으로든지 데이터를 얻을 준비가 되어 있어야 한다.




위로


참고자료




위로


필자소개


Jack Shirazi는 JavaPerformanceTuning.com의 디렉터이자 Java Performance Tuning, 2nd Edition (O'Reilly)의 저자이다.



Kirk Pepperdine은 Java Performance Tuning.com의 최고 기술 경영자(CTO)이며 객체 기술과 퍼포먼스 튜닝 분야에서 15년 동안 일해왔다. Ant Developer's Handbook을 공동집필 했다.

1555 view

4.0 stars