JAVA의 특징
❗️ JVM은 플랫폼에 종속적이다.
💡 객체지향 프로그래밍이란?
객체를 만들기 위한 설계도인 클래스를 작성하고,
객체와 객체를 연결하여 프로그램을 만든다.
Java는 객체지향언어가 가져야 할 캡슐화, 상속, 다형성 기능을 갖추고 있다.
애플리케이션이 실행될 때 모든 객체가 생성되는 것이 아니라,
각 객체가 필요한 시점에 클래스를 동적으로 로딩하여 사용한다.
➡️ 실행 시 연결된 부분에 대한 판단이 필요하므로 '비교적' 속도가 느리다.
위에서 말한 동적로딩의 영향도 있지만,
자바는 우리가 흔히 작성하는 코드가 한 번에 컴파일 돼서 컴퓨터가 알아들을 수 있는 기계어로 번역이 가능한 것이 아니다.
💡 .java ➡️ .class ➡️ 기계어
javac에 의해 .java파일이 컴파일 되어 .class(자바 바이트 코드)로 바뀐 후 JVM에 의해 기계어로 번역되고나서 실행한다.
이렇기에 C나 C++ 같이 컴파일 단계에서 만들어지는 기계어보다 비교적 속도가 느리다. 하지만, 바이트 코드를 기계어로 변환해주는 JIT(Just In Time)컴파일러 같은 기술 적용으로 JVM의 기능이 향상되어 속도의 격차가 많이 줄었다.
JAVA의 동작 방식
💡 자바 바이트코드: JVM이 이해할 수 있는 코드로, 컴퓨터는 읽을 수 없는 반기계어이다. 자바 바이트 코드의 각 명령어는 1바이트 크기의 Opcode와 추가 피연산자로 이뤄져있다.
자바 인터프리터(Java Interpreter): 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다. 하나하나의 실행은 빠르지만, 전체적인 실행 속도를 보면 비교적 느리다는 단점을 갖고있다.
JIT 컴파일러 (Just In Time Compiler): 인터프리터의 단점을 보완하기 위해 도입된 방식으로 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경. 그 후에는 더이상 인터프리팅 하지 않고 바이너리 코드로 직접 실행하는 방식.
💡 바이트 코드 전체가 컴파일 된 바이너리 코드들 실행하는 것이기 때문에, 전체적인 실행속도는 인터프리팅 방식보다 빠르다.
☑️ 런타임 데이터 영역은 JVM의 메모리 영역으로, 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.
❗️ 이 영역의 데이터는 프로그램의 시작부터 종료될 때까지 메모리에 남아있으므로, 전역 변수는 어디서든지 사용이 가능하다.하지만 이렇기에 전역 변수를 무분별하게 많이 사용하다 보면 메모리가 부족할 우려가 있다.
모든 객체들의 정보(referece type)를 저장한다. 이 때 변수는 Stack영역의 공간에서 실제 데이터가 저장된 Heap영역의 참조값(reference value, hash code / 메모리에 저장된 주소를 연결해주는 값)을 new 연산자를 통해 리턴 받는다.
다시 말하면, 실제 데이터를 갖고 있는 Heap 영역의 참조 값을 Stack 영역의 객체가 갖고있다.
이렇게 리턴 받은 참조 값을 갖고 있는 객체를 통해서만 해당 인스턴스를 핸들할 수 있다.
이러한 Heap Area도 Method Area와 같이 오직 하나의 영역만 존재하고, 공유할 수 있으므로 다른 스레드에서 접근이 가능하다.
Heap Area에 저장된 데이터가 더이상 사용이 불필요하다면, 메모리 관리를 위해 JVM의 GC(가비지컬렉터)에 의해 알아서 해제된다.
메서드 내에서 정의하는 primitive type에 해당되는 지역변수(매개 변수 및 블럭문 내 변수 포함)의 데이터 값이 저장되는 공간이다
프로그램이 아닌 메서드의 시작과 종료를 함께한다. 즉, 메서드가 호출될 때 메모리에 할당되고 종료되면 메모리에서 해제된다.
모든 스레드에 대해 JVM은 여기에 저장된 하나의 런타임 스택을 만드는데, 이는 메서드 호출 시마다 각각의 스택 프레임(해당 메서드만을 위한 공간)이 생성된다고 보면 된다.
즉, 생성되는 스레드는 각자 고유의 stack 영역을 갖고 있고, static과 heap영역은 공유할 수 있다.
스레드의 현재 실행 영역의 주소를 저장하고, 각 스레드에는 별도의 PC Register를 갖고 있다.
모든 스레드에 대해 별도의 Native Stack을 생성한 후 Native Method 정보를 저장한다.
일반적인 메서드를 실행하는 경우 JVM 스택에 쌓이다가 해당 메서드 내부에 Native 방식을 사용하는 메서드 (C, C++로 작성된 메서드)가 있다면 해당 메서드는 Native Method Stack에 쌓인다.