메모리 누수는 gem 사용자에게 골칫거리입니다. 추적하기 어렵고 인프라 비용이 많이 들 수 있습니다.
C 확장 내의 메모리 누수는 더욱 심각합니다. Ruby에서 누수를 찾는 방법에 대한 많은 도구와 기사를 볼 수 있습니다. 그러나 C에서는 내부에 동일한 액세스 권한이 없습니다.
rb_funcall의 순진한 사용법 메모리 누수가 발생할 수 있습니다. rb_protect를 사용하는 것이 훨씬 좋습니다. 대신. 따라서 C 확장 작성자라면 gem을 사용할 개발자를 위해 읽어보시기 바랍니다.
시작해 보세요!
rb_funcall의 문제 그리고 C
rb_funcall Ruby와 라이브러리의 C 부분 간에 상호 작용해야 하지만 약간의 C만 작성하면 되는 경우 훌륭한 도구가 될 수 있습니다.
그러나 rb_funcall을 실행하면 , 당신은 더 이상 모든 것이 간단한 C에 있지 않습니다. 호출된 함수의 경우 진흙탕에 빠질 수 있습니다:
- 런타임 중에 정의가 완전히 변경됩니다.
- 전화 걸기
1번이 가장 잡기 쉽습니다. 세그폴트가 발생할 가능성이 높으며 테스트 스위트가 충분히 완성되면 게시하기 전에 이를 포착해야 합니다.
그러나 후자는 메모리 누수를 일으키고 코드베이스를 읽기 어렵게 만들 수 있습니다. 지금부터 살펴보겠습니다.
C 메모리 누수를 유발하는 Ruby 인상
Ruby의 발생 메커니즘은 한 범위에서 오류를 포착한 첫 번째 상위 코드 부분 사이를 점프합니다. 이는 longjmp를 사용하여 MRI에서 구현됩니다. 및 setjmp .
이것이 어떻게 구축되는지에 관심이 있다면 Ruby 해킹 가이드의 평가자 장을 읽어보세요. 간단히 말해서 begin..ensure를 사용하면 차단해, 너 setjmp() , 그리고 이 블록 내에서 레이즈하면 longjmp() 저장된 위치로 이동합니다.
따라서 rb_funcall로 함수가 발생하면 , C 코드는 결코 실행되지 않은 후에 호출됩니다.
아래 예는 잠재적인 누출을 보여줍니다. json_parse인 경우 올리면 누출됩니다.
물론 위의 예는 약간 어리석습니다. 해제 및 Ruby 처리 부분을 뒤집을 수 있습니다. 그러나 이것이 항상 가능한 것은 아니며, 기능 본체가 길어질수록 더 얽힐 수 있습니다.
begin..ensure 사용 루비에서
Ruby를 사용하는 경우 대신 begin..ensure를 사용하여 위의 예를 작성할 수 있습니다. :
이 API는 C에서도 rb_rescue로 사용할 수 있습니다. 및 rb_ensure :
하지만 이는 다소 번거로운 작업이므로 rescue를 추가하려는 경우 파티를 차단하면 읽기가 훨씬 어려워집니다. begin..rescue..ensure..end를 사용하려면 Peter Zhu의 'A Rubyist's Walk Along the C-side(Part 8):Exceptions &Error Handling'을 읽어 보시기 바랍니다. C의 API
rb_protect 사용 C의 경우
또 다른 옵션이 있습니다. 먼저, Ruby에서 어떻게 보이는지 살펴보겠습니다:
이는 Ruby에서는 이상해 보이지만 C에는 매우 적합한 작업 흐름입니다. MRI에는 이를 위한 API(rb_protect)가 있습니다. , C 함수는 다음과 같습니다:
위의 방법은 모든 것을 해제한 후 Ruby 오류를 다시 발생시킵니다.
빈 rescue를 사용하여 오류를 무시하도록 선택할 수도 있습니다. Ruby의 블록:
경고: 오류를 발생시키지 않으면 rb_set_errinfo(Qnil) 사용자가 알아야 할 오류에 대한 정보를 계속 제공하지 않도록 하는 단계가 중요합니다.
또는 rescue My::Error와 같이 조건부로 오류를 발생시키도록 선택할 수 있습니다. :
실제로 rb_errinfo()을 고려해 볼 수 있습니다. $!와 동일 전역변수입니다.
모두 훌륭하지만 rb_funcall 하나로 요약하면 오직 API를 단순화할 수 있습니다.
rb_protect 사용에 대한 전반적인 아이디어 함수가 있을 때 API를 올려 가독성을 높이기 위함입니다. 함수가 발생할 수 있는지 여부를 확인할 필요가 없으며, 발생할 수 있다고 가정하고 상태를 사용하여 작업합니다.
rb_protect_funcall 제안
rb_funcall을 분리해 보겠습니다. , 유일한 위험이기 때문입니다. 사용 방법. 이를 수행하는 API는 다음과 같습니다:
이 API는 rb_funcall과 동일합니다. , state 포함 rb_protect에서 . 따라서 사용법은 매우 간단합니다:
이 API는 아직 Ruby에서 사용할 수 없으며 앞으로도 없을 수도 있습니다. RGeo(MIT 라이센스)에서 가져오실 수 있습니다.
실제 사례
실제 예를 보려면 최근 rb_protect 전체 버전으로 전환한 RGeocodebase를 읽어 보시기 바랍니다. . rgeo_convert_to_geos_geometry와 같은 일부 기능도 있습니다. , 더 간단한 사용을 위해 이 상태를 전파합니다. 이 기능은 탐색을 시작하기에 좋은 곳입니다.
RGeo에서 이슈를 열어 우리가 내린 선택에 대해 토론해 보세요.
마무리
이 게시물에서는 rb_funcall 사용에 대해 경고했습니다. C를 사용하면 메모리 누수가 발생할 수 있습니다. begin..ensure를 사용하여 탐색했습니다. 또는 rb_protect 대신.
즐거운 코딩 되세요!
추신 Ruby Magic 게시물이 보도되는 즉시 읽으려면 Ruby Magic 뉴스레터를 구독하고 단 하나의 게시물도 놓치지 마세요!
율리스 부오노모
우리의 객원 저자인 Ulysse는 대부분의 시간을 전 세계를 여행하는 데 바치는 전직 Ruby 개발자입니다. 그는 여가 시간을 RGeo와 Ruby에 전념하며 Ruby의 내부를 만지는 것을 좋아합니다.
Ulysse Buonomo의 모든 기사