결론부터 말하자면 자바는 컴파일 언어, 인터프리터 언어의 특징을 모두 가진 하이브리드 언어이다.
이 구조에 대해 공부하기 앞서서 컴파일과 인터프리터 언어가 무엇인지 간단하게 짚고 넘어 가보고자 한다.
소스 코드를 컴파일러라는 도구를 이용해 기계어(바이너리 코드)로 번역한 후 실행하는 프로그래밍 언어
소스 코드 작성 -> 컴파일 단계 -> 실행 파일 생성 -> 실행
컴파일 언어는 전체 코드를 미리 번역하여 빠른 실행과 오류 검출을 파악할 수 있는 환경을 제공하지만, 플랫폼 의존성과 긴 컴파일 시간이 단점이 될 수 있다.
소스 코드를 컴퓨터가 한줄 한줄 번역하고 실행하는 프로그래밍 언어
소스 코드 작성 -> 인터프리터에 의한 해석 및 실행 -> 결과 출력
인터프리터 언어 실행시 즉석에서 번역되어 개발 및 디버깅에 용이하고 플랫폼 독립적인 장점을 지녔지만, 실행속도가 느리고 오류 검출이 늦어 질 수 있다.
자바가 이러한 하이브리드 언어의 만들고자 했던 배경을 지식을 알아 보았다.
java는 1991년 제임스 고슬링(James Gosling)과 선 마이크로시스템즈(Sun Microsystems) 개발팀이 만든 Oak 프로젝트에서 시작되었다. 이 프로젝트의 목적은 가전제품(셋톱박스, TV, 휴대용 기기 등)에서도 실행가능한 언어를 만드는 것이 었다.
당시 프로그래밍 언어들은 대부분 OS에 종속적이어서 플랫폼 독립적인 프로그램을 만들기 어려웠다. C, C++ 같은 언어는 각 OS에 맞게 별도로 컴파일해야 했기 때문에, 다양한 기기에서 실행하려면, 각 환경마다 따로 개발해야 하는 불편함이 있었다.
이를 해결하기 위해, 한 번 작성하면 어디서든 실행할 수 있는 (Write Once, Run AnyWhere, WORA) 언어가 필요했다.
해결하고자 한 문제
당시 C / C++ 과 같은 언어는 특정 OS에 맞게 기계어로 변환되었기 때문에 하나의 프로그램을 여러 플랫폼에서 실행하려면 각각 별도로 컴파일을 해야 했다.
* C , C++이 각각 별도로 컴파일 해야했던 이유?
C , C++은 OS와 직접 소통하는 native language이다.
즉, 작성된 코드가 운영체제와 하드웨어에서 바로 실행되도록 기계어로 변환된다.
하지만 문제는 각 운영체제마다 cpu 구조(아키텍처),
시스템 API(함수 호출 방식)이 다르기 때문에,
한번 컴파일한 실행 파일을 모든 OS에서 사용할 수 없다.
java의 해결 방법
java는 JVM(Java Virtual Machine)을 사용하여 바이트 코드(중간 코드)로 변환한 후, JVM에서 실행하도록 만들었다.
즉, 자바 코드는 한번만 컴파일하면 모든 OS에서 실행 가능할 수 있다.
컴파일 방식의 한계
기존 컴파일 언어는 OS에 맞춰 별도로 컴파일 해야하는 번거로움이 있었다.
인터프리터 방식의 한계
기존 인터프리터 언어는 실행 속도가 느리다는 단점이 있었다.
C, C++ 같은 언어는 직접적으로 OS의 메모리에 직접 접근 가능하다. 따라서 버퍼 오버플로우와 같은 보안 문제가 발생할 수 있다.
* 버퍼 오버플로우
프로그램이 할당된 버퍼의 크기보다 더 많은 데이터를 입력받아
버퍼의 경계를 넘어서 데이터를 기록하는 상황,
이로 인해 인접한 메모리 영역이 덮여쓰여
예상치 못한 동작이나 프로그램 충돌의 위험이 발생할 수 있으며,
심각한 경우 악의적인 사용자가 이를 이용해서
임의의 코드를 실행하여 보안 취약점으로 이어질 수 있다.
JIT는 JVM 내부에서 실행되는 컴파일러이다.
프로그램 실행 중간에 필요한 부분만 실시간으로 컴파일해서 기계어로 변환하는 방식이다.
자바 소스 코드를 바이트 코드로 변환해준다.
JVM이 바이트 코드를 인터프리터 방식으로 실행하도록 해준다.
JIT가 바이트 코드를 네이티브 코드로 변환시켜준다.
