Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Ruby 메모리 누수 이해 및 수정:종합 가이드

메모리 누수에 관해 두 부분으로 구성된 이 시리즈의 첫 번째 부분에서는 Ruby가 메모리를 관리하는 방법과 가비지 수집(GC)이 작동하는 방식을 살펴보았습니다.

더 많은 메모리를 갖춘 강력한 시스템을 구입할 수 있으며 사용자가 눈치채지 못할 정도로 앱이 자주 다시 시작될 수도 있지만 메모리 사용량은 중요합니다.

할당 및 가비지 수집은 무료가 아닙니다. 누출이 있는 경우 앱을 구축한 작업을 수행하는 대신 가비지 수집에 더 많은 시간을 소비하게 됩니다.

이 게시물에서는 메모리 누수를 발견하고 진단하는 데 사용할 수 있는 도구에 대해 자세히 살펴보겠습니다.

계속하자!

Ruby에서 누출 찾기

누출을 감지하는 것은 매우 간단합니다. GC을 사용할 수 있습니다 , ObjectSpace , APM 도구의 RSS 그래프를 통해 메모리 사용량 증가를 확인할 수 있습니다. 하지만 누출이 있다는 사실을 아는 것만으로는 문제를 해결할 수 없습니다. 그것이 어디서 오는지 알아야 합니다. 실제 수치로는 그런 사실을 알 수 없습니다.

다행스럽게도 Ruby 생태계에는 해당 숫자에 컨텍스트를 연결하는 몇 가지 훌륭한 도구가 있습니다. 2개는 memory-profiler입니다. 그리고 derailed_benchmarks .

memory_profiler 루비에서

memory_profiler gem은 매우 간단한 API와 할당된 객체의 클래스, 크기, 할당된 위치가 포함된 상세한(약간 압도적이긴 하지만) 할당 및 유지 메모리 보고서를 제공합니다. 이는 Leaky 프로그램에 추가하는 것이 간단합니다.

 

이와 유사한 보고서를 출력합니다.

 

여기에는 많은 정보가 있지만 일반적으로 allocated objects by location 그리고 retained objects by location 섹션은 누출을 찾을 때 가장 유용할 수 있습니다. 할당된 개체 수에 따라 정렬된 개체를 할당하는 파일 위치입니다.

  • allocated 객체는 report 내에 할당(생성)된 모든 객체입니다. 차단합니다.
  • retained 객체는 report가 끝날 때까지 가비지 수집되지 않은 객체입니다. 블록. 우리는 유출된 객체를 더 명확하게 볼 수 있도록 블록이 끝나기 전에 강제로 GC를 실행했습니다.

retained을 신뢰하는 데 주의하세요. 개체 수. 유출된 코드 중 report 내에 있는 부분이 무엇인지에 따라 크게 달라집니다. 차단합니다.

예를 들어 an_array 선언을 이동하면 report에 블록을 사용하면 코드가 누출되지 않는다고 생각하게 될 수도 있습니다.

 

결과 보고서 상단에는 보유된 개체가 많이 보고되지 않습니다(보고서 자체만).

 

derailed_benchmarks 루비에서

derailed_benchmarks gem은 주로 Rails 앱을 목표로 하는 모든 종류의 성능 작업에 매우 유용한 도구 모음입니다. 유출을 찾으려면 perf:mem_over_time를 살펴보고 싶습니다. , perf:objectsperf:heap_diff .

이러한 작업은 curl를 전송하여 작동합니다. 실행 중인 앱에 요청을 보내므로 우리의 작은 누출 프로그램에 추가할 수 없습니다. 대신, 메모리 누수를 일으키는 엔드포인트가 있는 smallRails 앱을 설정한 다음 derailed_benchmarks을 설치해야 합니다. 해당 앱에서요.

 
 

이제 bin/rails s을 사용하여 앱을 부팅할 수 있습니다. . curl까지 가능합니다 각 요청마다 유출되는 엔드포인트입니다.

 

이제 derailed_benchmarks를 사용할 수 있습니다. 실제 유출 상황을 확인해보세요.

perf:mem_over_time

이는 시간 경과에 따른 메모리 사용량을 보여줍니다(watch를 사용하여 누출된 스크립트의 테마 메모리 증가를 관찰한 방법과 유사). 및 ps ).

Derailed는 프로덕션 모드에서 앱을 부팅하고 반복적으로 엔드포인트(/)에 도달합니다. 기본적으로) 메모리 사용량을 보고합니다. 성장이 멈추지 않는다면 누출이 있는 것입니다!

 

참고 :Derailed는 테스트를 수행하기 위해 프로덕션 모드에서 Rails 앱을 부팅합니다. 기본적으로 require rails/all도 됩니다. 먼저. 이 앱에는 데이터베이스가 없으므로 이 동작을 DERAILED_SKIP_ACTIVE_RECORD=true로 재정의해야 합니다. .

다양한 엔드포인트에 대해 이 벤치마크를 실행하여 어떤 엔드포인트(ifany)가 유출되는지 확인할 수 있습니다.

perf:objects

perf:objects 작업에서는 memory_profiler을 사용합니다. 내부적으로는 생성된 보고서가 친숙해 보일 것입니다.

 

이 보고서는 누수된 메모리가 할당되는 위치를 좁히는 데 도움이 될 수 있습니다. 이 예에서는 보고서의 마지막 섹션인 Retained String Report — 우리의 문제가 무엇인지 정확하게 알려줍니다.

 

LeaksController에서 "ABC"가 포함된 10,000개의 문자열이 유출되었습니다. 온라인 3. 중요한 앱에서 이 보고서는 훨씬 더 크고 유지하려는 문자열(쿼리 캐시 등)을 포함하지만 이 섹션과 기타 '위치별' 섹션은 유출 범위를 좁히는 데 도움이 될 것입니다.

perf:heap_diff

perf:heap_diff perf:objects의 보고서가 있으면 벤치마크가 도움이 될 수 있습니다. 누출이 어디서 발생하는지 확인하기에는 너무 복잡합니다.

이름에서 알 수 있듯이 perf:heap_diff 3개의 힙 덤프를 생성하고 이들 간의 차이를 계산합니다. 덤프 사이에 보관된 개체 유형과 해당 개체를 할당한 위치가 포함된 보고서를 생성합니다.

 

2021년 Ruby 메모리 누수 추적도 읽어보세요. 무슨 일이 일어나고 있는지 더 잘 이해하기 위해.

보고서는 누출된 아기 앱을 위해 어디로 가야 하는지 정확히 알려줍니다. 차이점 상단에는 LeaksController에서 할당된 999991개의 보유 문자열 개체가 표시됩니다. 3번째 줄에.

실제 Ruby 및 Rails 앱의 누출

지금까지 우리가 사용한 예제가 실제 앱에 적용된 적이 없기를 바랍니다. 아무도 메모리 누수를 의도하지 않기를 바랍니다!

사소한 앱에서는 메모리 누수를 추적하기가 훨씬 더 어려울 수 있습니다. 보유된 객체가 항상 나쁜 것은 아닙니다. 가비지 수집 항목이 있는 캐시는 그다지 유용하지 않습니다.

하지만 모든 누출에는 공통점이 있습니다. 어딘가에 루트 수준 개체(클래스/전역 등)가 개체에 대한 참조를 보유하고 있습니다.

일반적인 예 중 하나는 제한이 없는 캐시 또는 제거 정책입니다. 정의에 따르면 캐시에 저장된 모든 개체는 영원히 남아 있으므로 메모리가 누수됩니다. 시간이 지남에 따라 이 캐시는 점점 더 많은 앱 메모리를 차지하게 되며 실제로 사용되는 비율은 점점 작아집니다.

게임에서 높은 점수를 가져오는 다음 코드를 고려해 보세요. 예전에 본 것과 비슷하네요. 이는 비용이 많이 드는 요청이며 변경 시 쉽게 캐시를 폐기할 수 있으므로 이를 캐시하려고 합니다.

 

@scores 해시가 완전히 선택 취소되었습니다. 모든 사용자에 대해 모든 최고 점수를 보유하도록 성장할 것입니다. 둘 중 하나가 많은 경우에는 이상적이지 않습니다.

Rails 앱에서는 아마도 Rails.cache을 사용하고 싶을 것입니다. 대신 합리적인 만료를 사용합니다(Redis의 메모리 누수는 여전히 메모리 누수입니다!).

Rails가 아닌 앱에서는 해시 크기를 제한하여 가장 오래되었거나 최근에 사용한 항목을 제거하려고 합니다. LruRedux 훌륭한 구현입니다.

이 누출의 보다 미묘한 버전은 제한이 있지만 키의 크기가 임의적인 캐시입니다. 키 자체가 커지면 캐시도 커집니다. 일반적으로 이것을 치지 않을 것입니다. 그러나 객체를 JSON으로 직렬화하고 이를 키로 사용하는 경우 사용자가 읽은 메시지 목록과 같이 사용량에 따라 증가하는 항목도 직렬화하고 있지 않은지 다시 확인하세요.

순환 참고문헌

순환 참조는 할 수 있습니다 가비지 수집이 가능합니다. Ruby의 가비지 수집은 "Mark and Sweep" 알고리즘을 사용합니다. 가변 너비 할당을 소개하는 프레젠테이션에서 Peter Zhu와 Matt Valentine-House는 이 알고리즘이 어떻게 작동하는지 훌륭하게 설명했습니다.

기본적으로 표시와 청소라는 두 단계가 있습니다.

  • 마킹에서 단계에서 가비지 수집기는 루트 개체(클래스, 전역 등)에서 시작하여 표시한 다음 참조된 개체를 살펴봅니다.

    그런 다음 참조된 모든 개체를 표시합니다. 이미 표시되어 있는 참조 객체는 다시 살펴보지 않습니다. 이는 더 이상 살펴볼 개체가 없을 때까지 계속됩니다. 즉, 참조된 모든 개체가 표시되었습니다.

  • 그런 다음 가비지 수집기는 청소 단계로 이동합니다. 단계. 표시되지 않은 개체는 모두 정리됩니다.

따라서 라이브 참조가 있는 개체는 계속 정리될 수 있습니다. 루트 개체가 결국 개체를 참조하지 않는 한 해당 개체는 수집됩니다. 이러한 방식으로 순환 참조가 있는 개체 클러스터는 여전히 가비지 수집될 수 있습니다.

애플리케이션 성능 모니터링:이벤트 타임라인 및 할당된 객체 그래프

이 시리즈의 첫 번째 부분에서 언급했듯이 모든 프로덕션 수준 앱은 일종의 APM(애플리케이션 성능 모니터링)을 사용해야 합니다.

직접 롤링하는 것을 포함하여 다양한 옵션을 사용할 수 있습니다(대규모 팀에만 권장됨). APM에서 얻어야 하는 주요 기능 중 하나는 작업(또는 백그라운드 작업)이 수행하는 할당 수를 확인하는 기능입니다. 좋은 APMtools는 이를 분석하여 컨트롤러, 뷰 등 할당이 어디서 오는지에 대한 통찰력을 제공합니다.

이를 흔히 '이벤트 타임라인'이라고 합니다. APM을 통해 타임라인을 더욱 세분화하는 사용자 정의 코드를 작성할 수 있다면 보너스 포인트가 됩니다.

Rails 컨트롤러에 대한 다음 코드를 고려해보세요.

 

APM이 보고할 때 '이벤트 타임라인'은 AppSignal의 다음 스크린샷과 유사할 수 있습니다.

Ruby 메모리 누수 이해 및 수정:종합 가이드

이를 계측하여 타임라인에서 할당을 수행하는 코드 부분을 확인할 수 있습니다. 실제 앱에서는 아마도 코드에서 덜 명확해질 것입니다😅

 

다음은 AppSignal에서 가져온 이벤트 타임라인의 예입니다.

Ruby 메모리 누수 이해 및 수정:종합 가이드

계측할 위치를 아는 것이 종종 파악하기 어려울 수 있습니다. 애플리케이션의 코드를 실제로 이해하는 것을 대체할 수 있는 방법은 없지만 '냄새' 역할을 할 수 있는 몇 가지 신호가 있습니다.

APM이 시간 경과에 따라 GC 실행 또는 할당을 표시하는 경우 스파이크를 찾아 히트되는 특정 엔드포인트 또는 실행 중인 특정 백그라운드 작업과 일치하는지 확인할 수 있습니다. AppSignal Ruby VM 매직 대시보드의 또 다른 예는 다음과 같습니다.

Ruby 메모리 누수 이해 및 수정:종합 가이드

이러한 방식으로 할당을 살펴보면 메모리 문제를 조사할 때 검색 범위를 좁힐 수 있습니다. 이렇게 하면 memory_profiler과 같은 도구를 훨씬 쉽게 사용할 수 있습니다. 및 derailed_benchmarks 효율적으로.

할당 및 GC 통계 추적과 같은 AppSignal Ruby gem의 최신 추가 사항에 대해 읽어보세요.

마무리

이 게시물에서는 memory_profiler을 포함하여 메모리 누수를 찾아 해결하는 데 도움이 되는 몇 가지 도구에 대해 살펴보았습니다. , derailed_benchmarks , perf:mem_over_time , perf:objects , perf:heap_diff , AppSignal의 이벤트 타임라인 및 할당 개체 그래프.

제1부와 함께 이 게시물이 Ruby 앱의 메모리 누수를 진단하고 분류하는 데 유용했기를 바랍니다.

우리가 사용한 도구에 대해 자세히 알아보세요:

  • memory_profiler
  • derailed_benchmarks
  • 누설된 Rails 앱

추가 세부 내용:

  • GC 모듈 문서
  • ObjectSpace 모듈 문서
  • 쓰레기 수집 심층 분석
  • 가변 너비 할당

즐거운 코딩 되세요!

추신 Ruby Magic 게시물이 보도되는 즉시 읽으려면 Ruby Magic 뉴스레터를 구독하고 단 하나의 게시물도 놓치지 마세요!