내 JVM 메모리에는 무엇이 있나요?
JVM의 메모리 사용량을 보여주는 그래프를 살펴보겠습니다.

이 그래프에서 우리는 512MB의 Java 힙 공간을 가진 애플리케이션을 봅니다. 이것은 주로 사용되는 메모리 공간입니다. Mendix 애플리케이션 자체. 마이크로플로에서 객체를 검색하는 경우, 데이터베이스에서 읽은 객체가 메모리에 보관되는 곳입니다.
따라서 Mendix 런타임은 또한 이 공간을 사용하여 로그인한 세션에 대한 Java 객체, 데이터베이스의 원시 데이터를 마이크로플로우 내부에서 사용할 수 있는 객체로 변환하는 중간 객체 또는 웹 브라우저의 데이터그리드에 표시해야 하는 데이터인 경우 와이어에서 직접 전달할 수 있는 객체를 저장합니다. 마이크로플로우 자체도 여기에 객체라는 사실을 알고 계셨나요? Mendix 런타임은 인터프리터이며 모델링된 애플리케이션의 사본을 메모리에 저장합니다.
이 메모리 공간의 여러 부분은 JVM 가비지 수집기에서 관리합니다. (또한 다음을 참조하세요. JVM 힙 문서)
앞서 보여준 예는 실제로 전혀 사용되지 않는 애플리케이션에서 가져온 것으로, 아마도 어떤 예약된 이벤트가 발생하여 많은 객체가 생성되고 그 직후에 가비지 수집이 수행됩니다.
JVM은 혼자가 아닙니다…
이제 이 Java 메모리도 운영 체제 자체의 시스템 메모리 내부에 맞아야 합니다. Mendix 클라우드의 경우 배포 포털의 모니터링 부분에서 이러한 그래프를 볼 수 있습니다.
운영 체제 메모리의 실제 사용량을 표시하는 그래프입니다. 이것은 단일 환경의 애플리케이션 프로세스를 실행하는 데 전념하는 소위 앱노드입니다. Mendix 애플리케이션(테스트, 승인 또는 프로덕션 환경).

꽤 맞는 것 같지 않나요? 약 500MB가 사용 중이므로 위에서 본 Java Heap일 것입니다. 데이터베이스는 다른 가상 머신에서 실행되므로 데이터베이스 사용량은 여기에서 볼 수 없습니다. 분명히 Mendix Java 프로세스에 맞게 1024MB 메모리가 있는 Linux 가상 머신을 프로비저닝했습니다.
무엇을 기다립니다?
이제 다른 예를 살펴보겠습니다.


잠깐만요... 왜 운영 체제에서 900MB만 사용해야 했는데 512MB가 넘는 메모리가 사용 중인가요? Mendix 그렇지 않으면 사용되지 않는 메모리를 활용하기 위해 애플리케이션과 함께 다른 프로세스를 비밀리에 실행하고 있을까요?
아니요, 그게 아닙니다. 정말입니다. 🙂 글쎄요, 알겠습니다... 애플리케이션 자체 외에도 항상 실행되는 몇 가지 추가 프로세스가 있는데, 예를 들어 두 개의 모니터링 에이전트가 있습니다. 하나는 추세 시스템에 데이터를 제공하고(이러한 그래프를 생성하기 위해) 다른 하나는 경보 시스템에 데이터를 제공합니다. 그리고 배포 포털에서 중지 또는 시작 버튼을 누를 때 신호를 받으면 애플리케이션을 중지하고 시작할 수 있는 프로세스가 있습니다.
하지만 그 모든 것이 첫 번째 예시에도 있었습니다. 그럼, 여기서 무슨 일이 일어나고 있는 걸까요…?
서버의 각 프로세스가 얼마나 많은 메모리를 차지하는지 표시할 수 있는 ps 명령의 출력을 살펴보겠습니다.
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
xxxxx 19137 1.8 84.7 1248796 864696 ? Sl 2014 1177:33 java -Dfile.encoding=UTF-8 -XX:MaxPermSize=128M -Xmx512M -Xms512M -Djava.io.tmp...
네, 실제로 864696kB의 메모리를 사용하는 것은 자바 프로세스입니다.
왜 귀찮아?
왜 이걸 신경 써야 할까요? 결국, 이 프로세스는 여전히 사용 가능한 1024MB 메모리에 들어맞습니다. 이 512MB 자바 프로세스가 어떻게 마법처럼 844MB로 커졌더라도요.
사실, 이런 종류의 동작은 몇 가지 문제를 일으킵니다. 왜냐하면 시간이 지남에 따라 시작되는 다른 프로그램이 서버에서 실행되는 것을 방해하기 때문입니다.
우리는 릴리스와 보안 업데이트를 합니다…
At Mendix, 우리는 정기적으로, 그리고 여러분이 생각하는 것보다 훨씬 더 자주, 호스팅 플랫폼에 대한 변경 사항을 출시합니다. 매주, 또는 더 일반적으로, 심지어 매주 몇 번, 우리의 전체 호스팅 플랫폼에 작은 변경 사항이 적용됩니다. 거대한 월간 릴리스에 새로운 기능과 버그 수정을 쌓아 올리는 대신, 프로덕션에 들어갈 준비가 되자마자 출시합니다.
그리고 그들은 달려야 합니다!
프로덕션에 변경 사항을 배포하기 위해, 우리는 주로 puppet과 Debian 패키지를 사용하여 고객 프로젝트를 실행하는 애플리케이션 및 데이터베이스 서버에서 소프트웨어를 업데이트합니다. 그 외에도, 모든 서버에 보안 업데이트가 있을 때마다 설치하는 unattended upgrades라는 프로그램이 있습니다.
따라서 이 Java 프로세스가 모든 비율로 성장하면 이러한 일이 발생하지 않도록 할 수 있습니다. 릴리스를 설치하지 않으면 애플리케이션이 나머지 배포 시스템과 호환성 문제가 발생할 수 있습니다. 보안 업데이트를 놓치는 것은 누구도 원치 않는 일입니다.
다행히도, 이 문제는 매우 제한된 수의 환경에서만 발생합니다(현재 전체 애플리케이션 환경의 0.53%).
리눅스 커널이 구출에 나섰습니다!
여기서 무슨 일이 일어나고 있는지 파악하고 이러한 동작을 수정하는 과정에서 첫 번째 단계는 상황에 대한 더 많은 통찰력을 제공하는 것입니다.
Java 프로세스가 512MB 힙 공간을 사용하도록 구성된 경우... 그게 무슨 뜻일까요? 위에 제시된 Java 힙 그래프는 Java 코드 내에서 사용되는 실제 객체를 저장하는 데 사용되는 메모리의 일부를 보여줍니다. 하지만 Java 프로세스를 실행하려면... Java 프로그램 자체를 시작해야 합니다. 이 프로그램도 메모리에 있어야 합니까? 이 JVM 힙 메모리 내부에 있습니까? 그리고 방금 프로젝트의 userlib 위치에 넣은 수많은 jar 파일은 어떻게 됩니까? 그 파일들은 어떻게 될까요?
이를 더 잘 이해하려면 Java 힙 공간을 보여주는 그래프뿐만 아니라 운영 체제에서 전체 Java 프로세스가 차지하는 메모리 공간을 보여주는 그래프가 있으면 좋겠습니다.
…자세한 메모리 사용 정보 제공
다행히도 Linux 커널은 proc 파일 시스템을 사용하여 단일 프로세스가 차지하는 메모리 주소 공간의 전체 개요를 검색할 수 있도록 합니다. 이전에 보여준 예제 Java 프로세스의 PID(프로그램 식별자)는 19137입니다. 즉, Linux 셸 프롬프트에서 다음 명령을 실행하여 Linux 커널이 이 프로세스의 메모리 할당에 대한 모든 것을 표시하도록 할 수 있습니다. cat /proc/19137/smaps
사용 가능한 smaps 정보를 사용하면 JVM 프로세스 내부에서 무슨 일이 일어나고 있는지 매우 정확하게 추측할 수 있습니다.
이 페이지에 표시된 첫 번째 애플리케이션 그래프에 속하는 새로운 JVM 프로세스 메모리 사용 그래프를 소개합니다.

아하! 메모리를 차지한 모든 것이 512MB Java 힙이 아니었습니다! 약 230MB에 불과했고, 다른 부분은 Java 객체 힙 외부에 할당된 메모리가 차지하는 것 같습니다. 그 중 일부는 영구 생성 및 코드 캐시로, JVM이 가비지 수집할 만큼 흥미롭지 않다고 생각하는 객체를 저장합니다. 애플리케이션이 실행되는 한 절대 사라지지 않기 때문입니다. 또 다른 부분은 네이티브 메모리 부분입니다.
이제 문제가 있는 애플리케이션에 속하는 그래프를 살펴보겠습니다.

이 시나리오에서 Java 힙은 실제로 활발하게 사용되므로 실제로 필요할 때 사용할 수 있다고 약속된 것이 아니라 운영 체제에 의해 실제로 할당된 것입니다.
여기서 중요한 점은 우리가 실제 운영 체제 메모리가 JVM 객체 힙에 의해 완전히 차지되었다고 생각했지만 사실은 그렇지 않았다는 것입니다.
상황이 정말 걷잡을 수 없게 되면…
이 블로그 게시물을 마무리하며, 정말로 건전한 행동의 경계를 넘은 상황의 예를 하나 들어보겠습니다.
여기서는 1024MB Java 객체 힙에서 512GB 객체 힙으로 업그레이드된 1MB Java 힙 구성이 있습니다.

불행히도 JVM 프로세스의 메모리 사용량이 폭발적으로 늘어났고, 이에 대처하기 위해 운영 체제 크기를 조정해야 했습니다.

새로운 JVM 프로세스 메모리 그래프는 여기서 무슨 일이 일어나고 있는지 보여줍니다.

계속하려면 ...
지난 몇 주 동안 이 문제에 대해 수행된 조사는 이미 증가하는 힙 메모리 부족 문제에 대한 여러 가지 가능한 해결책을 보여줍니다. 향후 블로그 게시물에서 이에 대해 자세히 설명하겠습니다. 앞서 언급한 릴리스 및 보안 업데이트 문제가 발생하는 고객 애플리케이션은 다음에서 전화를 기대할 수 있습니다. Mendix 단기적으로 이러한 문제를 어떻게 처리할 것인지에 대한 것입니다.
전달 방법
이 그래프를 그리기 위해, 저는 리눅스 proc 파일 시스템의 smaps 파일에 있는 모든 정보를 살펴보았습니다. 일부 비트를 역엔지니어링하고 몇 가지 교육받은 추측을 함으로써, 저는 우리가 추세를 만드는 데 사용하는 m2ee 모니터링 그래프 구성에 대한 확장을 만들었습니다. 리눅스 커널의 smaps 정보를 검사하는 모니터링 플러그인 코드는 내부에 있습니다. m2ee-tools 소스 코드. 그만큼 munin 플러그인 설명서 원하는 경우 이 플러그인을 직접 활성화하는 방법에 대한 몇 가지 힌트가 포함되어 있습니다.