티스토리 뷰

JVM(Java Virtual Machine)은 자바 가상 머신으로 자바 프로그램을 실행시키기 위하여 반드시 실행되어야 하는 기초가 되는 프로그램이다. 즉 자바 프로그램을 실행시킨다는 것은 JVM을 실행시키고 그 위에서 자바 프로그램을 실행시킨다는 의미가 된다. 따라서 JVM은 운영체제와 자바프로그램 사이를 연결해주는 중계자 역할을 한다.


이러한 특징으로 인해 자바 프로그램에서 오류가 발생해도 JVM만 다운될 뿐 전체 시스템에 데미지를 주지 않는다. 또한 플렛폼에 의존적이지 않게 모든 자바의 프로그램이 구동된다고 할 수 있다. 정확히 말하면 JVM이 설치될 수 있는 모든 플렛폼이라고 보는것이 더 나을것이다.


자바라는 언어는 기본적으로 동적할당을 프로그래머에게 맡기지 않는다. C언어에서 malloc(), realloc() 등의 함수를 통하여 프로그래머가 힙 영역에 동적할당하고 free() 함수를 통해 할당을 해제하는 작업을 프로그래머가 직접한다. 하지만 자바에서는 그 작업을 JVM에서 전적으로 맡고 스스로 수행한다. 이 기능을 가비지 컬렉션(Garbage Collection)이라고 부른다.


전반적인 메모리 관리도 같이 한다. 따라서 JVM은 다양한 데이터(정적 변수, 클래스 등)들을 잘 다루어야할 필요성이 생기게 된다. 그래서 JVM은 각 용도에 맞게 메모리를 나누어서 관리하게 된다.


대규모 프로젝트를 진행함에 있어서 JVM튜닝이 불가피 하다. 따라서 JVM의 메모리구조를 아느냐 모르느냐에 따라서 메모리를 더 최적화하여 사용할 수 있느냐 없느냐의 차이로 이어지게 된다. 이것은 결국 프로그램 전체의 성능에 영향을 미치는 부분이 된다. 따라서 우리는 JVM의 메모리 구조를 자세하게 알아야할 필요가 있다.




JVM 구조


Garbage Collector


가비지 컬렉션을 수행하는 모듈이다. 자바 프로그램이 실행중에 동적으로 생성한 객체가 모두 사용되었는지의 여부를 판단하고 할당된 메모리를 해제한다.



Class Loader


JVM안으로 클래스의 정보를 읽어들이는 역할을 하는 모듈이다. 프로그램 동작시 동적으로 클래스를 로드한다.



Execution Engine


클래스의 로딩이 완료되면, 자바의 바이트 코드를 읽어들어서 실제로 실행시키는 모듈이다.


Runtime Data Area


Java 프로그램이 실행될 때 JVM이 실행되면서 운영체제로 부터 할당받는 메모리이다. 이는 메모리의 용도에 따라 크게 5개로 나누어 진다.


1. Method Area(Static Area)


JVM이 읽어들인 클래스인터페이스들의 런타임 상수 풀이다. 즉 클래스의 구성요소인 정적 변수, 생성자, 메소드, 멤버 변수가 이 공간에 저장된다.


2. Heap Area


자바 프로그램이 실행되면서 동적으로 생성된 객체(new 연산자로 생성된 객체 또는 인스턴스)가 저장되는 공간이다. 이곳에 생성된 객체들은 다른 객체의 필드 또는 스택에 존재하는 다른 메소드에 의해 참조될 수 있다. 참조하는 변수가 사라진다면 이 객체는 필요없는 것으로 간주하고 Garbage Collector에 의해서 할당이 해제된다.

힙 영역은 그 기능이 다양해서 여러 부분으로 나누어진다. 객체가 처음 생성되면 저장되는 공간과 객체가 생성된 후 일정시간 사용되지 않으면 이동되어 가비지 컬렉션을 수행할 수 있도록 분류되는 공간으로 나누어서 관리한다.


3. Stack Area


메소드가 호출되면 이 영역에 할당된다. 먼저 들어온 것이 가장 마지막으로 나가는 구조(FILO)이다. 스레드가 생성되면 각 스레드는 스레드마다 하나의 스택을 할당받게 된다. 스택에는 메소드 수행시 발생되는 지역변수 또한 저장된다. 스택에서는 힙 영역의 객체를 참조할 수 있으며, 기본타입 변수는 스택에 직접 생성된다.(객체는 힙 역역)


4. PC Register


현재 수행중인 JVM 명령 주소를 가진다. CPU의 PC와 같은 역할이라고 할 수 있다.


5. Native Method Stack Area


자바 이외의 언어로 된 코드를 위한 스택이다. JNI(Java Native Interface)를 통해 호출되는 다른 언어의 코드를 수행하기 위해 존재한다.


위에서 설명한 개념을 실제 코드에서 확인해 보도록 하겠다.


<코드>

위 코드의 결과값은 어떻게 될까?

결과값은 아래와 같다.


<결과>

staticNum -> Static area (이하 초록색으로 표시)

localNum -> Stack area (이하 분홍색으로 표시)


정적 변수static area에 생성되어 모든 메소드에서 접근이 가능하다. 다른 클래스를 생성하여 이 변수에 접근한다면 그 클래스 마저도 정적 변수에는 접근할 수 있다. 따라서 add1, add2 메소드에서 staticNum이라는 수에 1을 더하는 것은 static area에 존재하는 공통된 변수 staticNum에 접근하여 덧셈을 시행하는 것이다.


localNum은 main 메소드에서 생성된 지역 변수이다. 지역 변수라는 것은 스택영역에 존재하면서 해당 메소드 안에서 생성되어 메소드가 사라지게 될 경우 함께 사라지게 되는 변수를 뜻한다. 따라서 add1이라는 메소드가 실행되면서 main 메소드에서 생성된 localNum 변수를 매개변수로 받아서 그 변수에 1을 더하고 출력한뒤 종료된다.


main 메소드에서 localNum은 0으로 초기화되어 있으므로, add1메소드는 0인 값을 가져와서 그 값에 1을 더한 후에 출력하고 메소드가 종료되면 그와함께 매개변수도 함께 사라지게 된다. 따라서 main에 있는 localNum에게는 아무런 영향이 없을 수 밖에없다. 그래서 1을 출력하고 뒤에 add2 메소드가 불려서 다시 값을 가져올때는 처음에 main 메소드에서 초기화한 0이라는 값을 다시 가져와서 1을 더하고 출력하여 결국 1이라는 결과가 나오는 것이다. 아래의 구조를 보면 이해하는데 도움이 될 것이다.





Heap area에 생성된 인스턴스에 접근하는 방식



Heap area에는 자바 프로그램이 실행중에 생성하는 객체들이 생성되는 메모리이다. 과연 힙 메모리에 존재하는 객체에는 어떤 방식으로 접근이 되는지 아래의 코드를 보면서 알아보도록 하겠다.


ExampleClass의 정보(멤버변수, 메소드, 생성자 등)는 처음에 자바 프로그램이 시작될때 static area(Method Area)에 적재된다. 그리고 실행중에 그 클래스의 정보를 통해 객체화하여 사용하는 방법이 일반적이다. 이때 객체화 된다는 의미는 붕어빵으로 생각하면, 붕어빵 틀(클래스)로 붕어빵(객체)을 만들어내는 것을 의미한다. 이제 코드로 넘어가보자.


ExampleClass라는 이름의 클래스를 참조하는 변수 ex가 생성되고 ex에는 new연산자로 객체를 생성하여 참조하도록 하는 모습이다. 여기서 주의할 점은 객체가 생성되는 장소는 heap area지만 그것을 참조하는 참조변수는 stack area에 존재하는 ex라는 지역변수이다. 따라서 스택영역에서 실행중에 객체를 생성하면 그 객체는 힙 영역에 할당되고 그것을 참조하는 변수는 스택 영역에 할당된다는 것을 혼동해서는 안된다.


그리고 ex에 null을 넣어 주어 참조값을 없애는 작업을 하고 있다. 이 작업이 진행되면 힙 영역의 객체를 가리키는 참조변수가 사라지게 된다. 즉 이 객체는 더이상 쓰임새가 없는 객체라는 것이다. 이러한 객체들을 청소해 주는것이 가비지 컬렉터의 역할이다.




JVM의 메모리의 크기는 변경이 가능할까?



위에서 메모리 구조를 면밀히 살펴 보았다. 그렇다면 우리가 자바 프로그램을 개발하는 도중에 각 메모리의 크기를 프로그래머가 자율적으로 할당하고 싶을 것이다.


예를들어 어떤 프로그램은 수많은 객체를 힙 영역에 생성하여 적은 수의 메소드를 스택영역에서 생성하여 운영할 수도 있고, 어떤 프로그램은 하나의 객체를 생성하여 그 객체에서 수많은 메소드를 올리고 내리는 유형의 프로그램도 존재할 것이라고 생각한다. 따라서 프로그램이 어떤 유형으로 구동되는지에 따라서 힙 영역의 크기, 스택 영역의 크기, 메소드 영역의 크기의 요구량을 최적화 하는 방법은 달라질 수 있다는 것이다.


그래서 메모리의 크기를 수동으로 설정하는 방법을 알아보았다. 대중적인 Java개발환경인 Eclipse환경을 통하여 설정해 보았다.


* 설정값의 종류

-Xmx<size> 초기의 Heap size

-Xms<size> 최고 Heap size

-Xss<size> Stack size


Heap영역의 크기는 초기 사이즈와 최고 사이즈가 존재하는 것으로보아 동적으로 크기가 변경된다는 것을 알 수 있다. 그리고 Stack영역의 크기는 고정된 크기로 볼 수 있겠다.



1. 설정 버튼을 클릭한다.




2. 설정하고 싶은 .java파일을 선택하고 Edit버튼을 클릭한다.





3. Arguments텝을 클릭한다.




4. 아래 VM arguments공간에 명령어를 입력하고 OK를 클릭한다.





결론



이렇게 JVM에 대하여 알아보았다. 솔직히 우리가 자바를 코딩하면서 이런 부분에 대하여 한번쯤은 깊숙히 생각해 볼 필요가 있지 않을까 하는 생각이 들었다. 내가 유능한 개발자가 되려면 단지 코딩만 잘하는 것이 아니라 이런 깊숙한 부분을 이해하고 있는 사람만이 어떠한 상황이 닥쳐도 그 상황에 유연하게 대처할 수 있는 유능한 개발자가 되지 않을까 하는 생각이 들었다.




참고



https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%EA%B0%80%EC%83%81_%EB%A8%B8%EC%8B%A0

http://huelet.tistory.com/entry/JVM-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B5%AC%EC%A1%B0

http://hoonmaro.tistory.com/19

http://limkydev.tistory.com/51

https://brunch.co.kr/@artiveloper/14

댓글
  • 프로필사진 호이짜 JVM의 기초지식에 대해서 잘 정리된것 같습니다. 잘보고 갑니다. ^^ 2018.06.27 14:10
  • 프로필사진 heyhyo 감사합니다^^ 2018.06.27 14:35 신고
  • 프로필사진 freestrokes 잘 정리하셨습니다.
    자바 기반의 프로젝트를 이클립스와 같은 IDE 환경이 아닌, 리눅스와 같은 server-side 환경에 배포하여 운영할 경우 시스템의 성향에 따라 옵션을 사용한 자바의 튜닝을 필요로 합니다.
    JDK1.7과 JDK1.8의 경우 OutOfMemoryError에 영향을 미치는 -Xms -Xmx -XX:PermSize -XX:MaxPermSize 옵션에서 차이를 보이고 Garbage Collector와도 연관이 있습니다.
    이 부분에 대해서도 알아보시면 좋을 것 같습니다:)
    2018.06.27 14:13 신고
  • 프로필사진 heyhyo 버전별로 설정 옵션이 다른 부분은 미쳐 생각하지 못했습니다. 좋은 정보 감사합니다! 2018.06.27 14:37 신고
  • 프로필사진 hoho3 오래전 글이지만 도움이 되었습니다 감사합니다 2021.04.25 16:00 신고
  • 프로필사진 heyhyo 감사합니다 :) 2021.05.06 17:21 신고
댓글쓰기 폼
공지사항
Total
282,840
Today
141
Yesterday
228
«   2021/12   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
글 보관함