메모리에 대해 알아보자(스프링은 메모리를 어떻게 최적화할까?)

Alex·2025년 2월 1일
0

CS를 공부하자

목록 보기
22/74

JVM의 메모리는 어떻게 사용될까?

JVM은 메모리를 크게

heap 메모리와 non-heap memory 두개의 카테고리로 분류한다.

heap 메모리는 애플리케이션에서 생성되는 객체들이 보관되는 곳이다. 이 객체들은 더는 다른 곳에서 참조되지 않을 때 가비지 컬렉터에 의해서 회수된다.

그래서, heap 메모리의 양은 애플리케이션이 동작하면서 계속 변동한다는 특징이 있다.

네티이브 메모리 영역은 위처럼 분류된다.

  • compressed class space: 로딩된 클래스들의 정보를 담아놓는 곳이다. 이 클래스들의 함수들도 로딩된다.
  • 스레드 : JVM 스레드에 의해 쓰이는 메모리다.
  • 코드 캐시: JLT에 의해 쓰이는 메모리다
  • GC: GC에 의해서 쓰이는 메모리다.
  • 심볼: 필드 이름이나 메서드 시그니처 등이 저장된다

두개의 차이점은?

heap 메모리와 달리, non-heap 메모리는 load되고서 크게 변경되지 않는다. (힙메모리는 계속 변동한다)

non-heap 메모리를 줄이려면, 클래스 로더가 가비지 콜렉터에 의해서 회수돼야 한다.

메모리 설정을 어떻게 해야해?

-Xmx16M처럼 최대 메모리를 설정하는 것만으로는 부족하고, 실제로는 더 복잡한 고려사항들이 있다.

JTI의 코드 캐시는 기본적으로 240MB까지 사용한다. 코드 캐시가 너무 작으면, JLT는 저장 공간이 부족해져서 퍼포먼스가 떨어진다. 그렇다고 너무 크면 메모리가 낭비된다.

도커 컨에니어에서 JVM을 사용할 땐, JAVA는 컨테이너의 메모리 limit을 알지 못하고 JVM의 사이즈를 할당한다. 이에 따라 non-heap 메모리는 너무 많이 할당하고, heap 메모리는 적게 할당하는 문제가 발생한다.

결국, 비힙 메모리는 잘 변동하지 않으니 거기에 맞춰서 할당하고, 나머지는 모두 힙 메모리에 할당하는 게 좋아보인다.

스프링의 메모리 최적화 방식

스프링은 힙/비힙 메모리를 고려해서 설계가 돼 있다고 한다.

비힙 메모리를 줄이기 위해서, 프레임워크를 최대한 범용적으로 만들었다. 대표적인 예로, 리플렉션을 사용해서 빈을 생성하고 주입하는 방식이다. 이렇게 하면, 코드의 양이 늘어나든 줄어들든 관계 없이, 애플리케이션이 관리하는 빈의 양은 변하지 않는다.

힙 베이스의 캐시를 사용해서, 애플리케이션 시작 시간을 빠르게 하면서-> 시작이 완료되면 캐시를 비운기도 한다. GC는 이 메모리를 쉽게 회수하는데, 이를 통해서 애플리케이션이 사용할 메모리를 빠르게 돌려줄 수 있다.

참고자료

Memory footprint of the JVM

profile
답을 찾기 위해서 노력하는 사람

0개의 댓글