어째서 JVM을 알아야 하는가?

반응형

이전에도 JVM관련해서 글을 작성한적이 있습니다.

 

JVM 겉핥기

java를 학습하게 되면 어떤것이 중요할까?객체지향도 중요하지만, java가 어떤것인지도 굉장히 중요하다.그니까 java가 어떤식으로 코드를 읽는지 그런것도 중요하다는거다.물론 나는 "코딩만 하

b-programmer.tistory.com

 

[면접 준비] JVM이란?

서론드디어 2차 프로젝트가 끝이 났다. 이번에도 테스트를 진행하지 못했다. 무엇이 문제였을까?도커도 하지못했고 문서화도 하지 못했다. 원래 다른 공부도 병행하면서 해야 되는데 시간이 없

b-programmer.tistory.com

 

[면접공부] java - jvm(2)

자바를 공부하면서 제일 중요하다고 생각하는 부분이 JVM이라고 생각하다.그렇다고 그전에 작성한 글을 똑같이 작성하는 건 아니구 JVM이 왜 중요하는지 부터 생각을 해야 한다.사실 JVM자체가 중

b-programmer.tistory.com

하지만 이상하게도 JVM에 대해 설명하라고 하면 생각보다 쉽지 않았습니다. 그래서 이번에는 생각을 바꿔서 JVM이 왜 필요한지 부터 생각해보겠습니다. 

만약, JVM이 존재하지 않는다면?

java나 kotiln을 실행하고 나면 .class라는 파일이 생성이 되어집니다. 일단 생각해야 할것이 .class는 누구를 위한 파일일까요?
바로 JVM입니다. .class 파일은 CPU가 직접 실행하는 기계어도 아니고 OS가 이해하는 실행 파일도 아닙니다.
단지, 바이트 코드이자 중간 산출물일 뿐입니다.

만약, .class파일을 생성해주지 않는다면, java는 OS별 네이티브 바이너리코드를 각각 생성해줘야 합니다. 
하지만 JVM은 이러한 문제를 해결해줬습니다. 

전체적인 맥락은 위와 같습니다. 
그림상에서는 JVM이 마치 window, Linux, Mac으로 변환을 시켜주는걸로 나오는데 사실은 OS마다 JVM구현이 조금씩은 다르다고 합니다. 만약, JVM이 존재하지 않는다면, 어떻게 될까요? JVM이 존재하지 않는다면, 자바는 OS마다 다른 실행 파일을 만들어야 하고,
개발자는 OS 차이를 직접 고려해야 합니다. JVM은 이 차이를 추상화하여 하나의 바이트코드로 모든 환경에서 실행 가능하도록 만듭니다.
이것이 바로 JVM이 필요한 이유입니다.

그렇다면, JVM은 어떻게 코드를 정리할까요?

간략하게 이야기해보면, JVM은 코드를 바로 실행하지 않습니다. 먼저 로딩 과정을 거칩니다.
.class 파일을 메모리로 올리고, ClassLoader를 통해 JVM 내부 영역에 적재하는 단계입니다.
이 시점에서는 아직 실행이 이루어지지 않습니다. 그 다음 단계는 검증 과정입니다. JVM은 바이트코드가 안전한지 확인합니다.

  • 타입이 올바른지
  • 스택 사용이 정상적인지
  • 잘못된 접근이 없는지

실행 전에 한 번 걸러내는 과정입니다. 이 검증 단계 덕분에 자바는 비교적 안정적인 실행 환경을 유지할 수 있습니다. 이후에 실제 실행 단계로 넘어갑니다. 인터프리터 방식으로 실행되기도 하고, 자주 실행되는 코드는 JIT 컴파일을 통해 네이티브 코드로 변환되어 실행됩니다.
그리고 이 모든 과정의 중심에는 메모리 관리가 있습니다. 객체는 계속 생성되고, 사용이 끝난 객체는 정리되어야 합니다.
이 역할을 JVM이 수행합니다. Garbage Collector를 통해 필요 없는 객체를 회수하고, 프로그램이 안정적으로 동작할 수 있도록 메모리를 관리합니다. 결국 JVM은 단순히 코드를 실행하는 도구가 아닙니다. 코드를 로딩하고, 검증하고, 실행하며, 실행 중에도 계속해서 자원을 관리하는 런타임 체계입니다.

그렇다면 JVM은 메모리 관리를 왜 하는걸까요?

단순하게 생각하면 객체가 생성되기 때문일겁니다. 본질적으로 생각해보겠습니다.

코드는 디스크에 존재하지만, 실행되는 순간 메모리에 올라옵니다.
객체는 생성되고, 참조되고, 더 이상 필요 없어지기도 합니다. 이 모든 과정이 메모리 안에서 이루어집니다.

JAVA_OPTS: "-Xms256m -Xmx512m"

이 설정은 단순한 숫자처럼 보이지만, 사실은 JVM이 사용할 수 있는 메모리의 범위를 정의하는 것입니다.

  • -Xms256m → JVM이 시작할 때 확보하는 초기 힙 크기입니다.
  • -Xmx512m → JVM이 사용할 수 있는 최대 힙 크기입니다.

즉, 이 프로그램은 최대 512MB의 Heap 메모리 안에서만 살아야 합니다.
여기서 2가지 질문을 할 수 있습니다.

1. Heap 메모리가 512MB가 넘어가면 사용할 수 없는건가?
2. Heap 메모리의 최소 크기는 왜 확보해야 하는걸까?

결론부터 말하자면, Heap 메모리는 512MB가 넘어갈 수 없습니다. 

JVM은 이 값을 상한선으로 인식합니다. 객체가 아무리 많이 생성되더라도, Heap은 512MB를 초과하지 않습니다.
만약 GC 이후에도 충분한 공간을 확보하지 못한다면, 결국 OutOfMemoryError가 발생합니다.
다만 여기서 한 가지 오해하면 안 되는 부분이 있습니다.
512MB는 Heap 영역의 최대 크기일 뿐입니다. JVM이 사용하는 전체 메모리는 512MB보다 클 수 있다는 점~

두 번째 질문을 생각하기전에 우리는 core-size와 max-size에 대해 학습한적이 있습니다.
이 개념을 간략하게 설명하자면, 동시에 동작할 수 있는 스레드의 개수를 제한하는 설정입니다.

core-size는 기본적으로 유지되는 스레드 수이며, max-size는 부하가 증가했을 때 확장 가능한 최대 스레드 수입니다.
즉, 시스템이 감당할 수 있는 범위 안에서 동시성을 통제하기 위한 장치입니다.
이 이야기를 꺼낸 이유는 Heap 메모리를 설정하는 방식과 core-size의 철학이 유사하기 때문입니다.

Heap에서도 마찬가지입니다.

  • -Xms는 기본적으로 확보하는 메모리입니다.
  • -Xmx는 확장 가능한 최대 메모리입니다.

스레드와 메모리는 자원의 종류만 다를 뿐, 결국 동일한 원칙을 따릅니다. 최소는 확보하고, 필요하면 확장하되, 반드시 상한선을 둔다.

왜 이런 구조를 가지는 걸까요? 그 이유는 단순합니다. 자원은 무한하지 않기 때문입니다.
스레드를 무제한으로 늘리면 CPU가 감당하지 못하고, Heap을 무제한으로 늘리면 시스템 메모리를 잠식하게 됩니다.
결국 JVM은 자유로운 확장을 허용하는 대신, 통제된 확장을 선택한 구조입니다.
이것이 core-size와 Heap 설정에대한 철학이 유사하다고 생각하는 이유입니다.

어찌되었든 JVM에서 메모리를 관리하는 이유는 프로그램을 지키기 위한 전략입니다.
프로그램은 객체를 계속 생성합니다. 그리고 힙 메모리는 무한하지 않습니다.
예를 들어 -Xmx512m로 설정했다면 힙은 512MB를 넘을 수 없습니다.
객체가 계속 생성되어 이 한계에 가까워지면 JVM은 Garbage Collection을 수행합니다.
여기서 중요한 점은, GC는 힙을 확장하는 것이 아니라 사용되지 않는 객체를 제거하여 공간을 확보하는 역할을 한다는 것입니다.

  • 힙의 상한선은 변하지 않습니다.
  • GC는 그 안에서 살아남기 위한 정리 작업입니다.

만약 GC 이후에도 충분한 공간이 확보되지 않는다면 결국 OutOfMemoryError가 발생합니다.
따라서 JVM의 메모리 관리는 무한히 확장하는 전략이 아니라 제한된 공간 안에서 생존하는 전략 입니다.

결론

간략하게 힙메모리에 대해 조금더 생각을 해보는 시간을 가졌습니다. 다만 이 내용이 다소 부실하다고 생각이 들어집니다. 딱딱하게 말하고 싶지않기도 해서 어떻게 적을지 계속 고민했던거 같습니다. 본질적으로 JVM을 왜 사용하는지에 대해 알아봤습니다. 

 

 

반응형

댓글

Designed by JB FACTORY