실제로 이를 이용해서 프로그래밍 역시 가능합니다. 하지만 수정을 해야 하는 상황을 가정해봅시다.
만약 위와 같은 코드가 몇만 줄이 있을 때, 딱 원하는 부분만 수정하기에는 정말 어려운 일입니다.
이는 고스란히 개발자들의 진입장벽에도 영향을 주게 됩니다.
이 문제를 조금이나마 해결하고자, 16진법으로 변경해 보지만...
8b 45 f8
83 c4 0c
...
여전히 이해하기에는 어렵습니다. 좀 더 획기적인 해결책이 필요합니다.
문제점 2. CPU마다 다르다
진입 장벽도 문제이지만, 이 두 번째 문제점이 훨씬 중요합니다. CPU마다 사용하는 기계어가 다르다는 것입니다.
이 때문에, 개발자들은 각 CPU마다 기계어를 새롭게 짜야하고, 또 인기 없는 CPU들은 금방 사장될 것입니다.
너무나도 비효율적이죠. 이를 통일할 순 없을까요?
어셈블리어
16진법으로도 해결이 안 되자 개발자들은 고민을 하기 시작합니다.
"이렇게 어려운 기계어들을 사람이 이해하기 쉽게 변경할 수는 없을까?"
개발자들은 기계어들의 명령들을 사람이 쓰는 단어에 매칭 시키기 시작합니다.
add eax, DWORD PTR [ebp-4]
...
이를 통해 훨씬 진입장벽이 낮아졌습니다. 하지만 문제가 생깁니다.
어셈블리어는 사람들이 이해하기에는 적합하지만, 컴퓨터(기계)가 이해하기에는 어렵다는 것입니다.
그래서 개발자들이 어셈블리어를 입력하면, 이를 기계어로 바꿔 줄 프로그램이 필요합니다. 이를 어셈블러라고 합니다.
고급 언어
이 어셈블리어는 사람들이 사용하는 단어로 변경을 했다지만, 문법적인 구조는 여전히 기계어에서 벗어나지 못했습니다.
이를 최대한 사람이 이해할 수 있게 변경한 언어들이 고급 언어입니다.
고급과 저급이라는 말이 언어의 수준을 의미하는 것은 아닙니다. 사람의 언어에 가까우면 고급언어, 기계어에 가까우면 저급 언어라고 합니다. 현재 개발자들이 많이 사용하는 Java, C++ 등을 고급 언어라고 합니다.
고급 언어 역시 어셈블리어처럼 컴퓨터가 이해하지 못합니다. 이 문제를 해결하기 위해 어셈블러와 같은 기능을 사용하는 프로그램이 필요합니다. 고급언어에서 이 기능을 하는 프로그램을 '컴파일러'라고 합니다.
Java를 예시로 들어봅시다. Java는 컴파일 언어이기 때문에 이를 실행하기 위해서는 컴파일러가 필요합니다.
Java를 입문할 때 Shell에서 실행을 해보는 실습을 해 보셨을 겁니다.
javac HelloWorld.java
java HelloWorld
그때 소스코드를 짠 후 그대로 실행하지 않았습니다.
javac라는 절차를 한번 거치고, java를 실행했었죠. 이때 javac가 바로 컴파일 단계입니다!
그림1. java 소스코드(위), 컴파일 후 클래스파일(아래)
기계어보다는 좀 이해할만하죠? 이렇게 고급 언어를 가상머신이 이해할 수 있는 정도로만(중간 언어 or 코드) 컴파일한 것을 바이트코드라고 합니다!
하지만 컴파일만 하는 게 능사가 아닙니다.각 OS가 이해할 수 있게 바이트코드를 추가적으로 번역(컴파일) 해 주어야 합니다. (이를해석 머신이라 합니다.)
그림2. JVM의 역할
다음 그림과 같이 각 OS들은 각기 다른 언어체계를 가지고 있습니다. JVM은 이렇게 바이트코드를 각 OS에 맞게 번역해주는 기능을 제공하고 있습니다. JVM의 추가적인 동작 덕분에 개발자들은 OS를 고려하지 않아도 됩니다.
이렇게 JVM은실제 컴퓨팅 머신과 마찬가지로명령어 세트가 있으며 런타임에 다양한 메모리 영역을 각OS에 맞게 조작합니다. 그래서JVM을 추상 컴퓨터 머신이라고도 합니다.
하지만 잃는 것도 있습니다.OS를 고려하지 않아도 된다는 편리함을 얻은 대신 성능이 다른 언어들보다 더 떨어지게 됩니다.
2. 자바 바이트코드
JVM는 특정 바이너리 형식인 클래스 파일 형식(=자바 바이트 코드)만을 알고 있습니다. 덕분에 JVM은 자바 바이트코드의 형식만 지킨다면 어떤 언어든지 간에 JVM에서 실행할 수 있다는 것입니다.
The Java Virtual Machine knows nothing of the Java programming language, only of a particular binary format, the class file format. A class file contains Java Virtual Machine instructions (or bytecodes) and a symbol table, as well as other ancillary information.
덕분에 JVM은 '추상' 컴퓨터 머신이라고도 불립니다. (일부는 JVM에서 Java를 뜻하는 J를 빼라고 할 정도입니다.)
그럼 이제 JVM에서 실행하기 위해 지켜야 할 클래스 파일 형식(Class File Format)에 대해 알아봅시다!
그림3. 클래스 파일 구성 요소
클래스 파일 형식은 크게 10가지로 나뉩니다.
대분류
소분류
설명
매직넘버
매직 넘버
이 파일이 클래스 파일임을 나타내는 인식표 (0xCAFEBABE)
클래스 파일 포맷 버전
메이저 버전
클래스 파일의 메이저 버전
마이너 버전
클래스 파일의 마이너 버전
상수 풀
상수풀 카운트
클래스 상수들이 모여있는 위치, 여기서는 개수를 설명
상수풀[카운트-1]
클래스 상수들이 모여있는 위치를 설명
액세스 플래그
액세스 플래그
클래스 또는 인터페이스에 대한 액세스 권한 및 속성을 나타내는 데 사용되는 값
클래스
this 클래스
클래스에 포함된 this 타입 계층을 나타내는 요소
슈퍼 클래스
슈퍼 클래스가 없다면 0, 있다면, 상수 풀 테이블에 대한 유효한 인덱스 표기
인터페이스
인터페이스 카운트
해당 클래스 또는 인터페이스 유형의 직접적인 슈퍼 인터페이스 수
인터페이스[카운트-1]
인터페이스 배열의 각 값은 상수 풀 테이블에 대한 유효한 인덱스
필드
필드 카운트
fields 테이블의 field_info 구조의 수를 제공
필드[카운트 - 1]
이 클래스 또는 인터페이스의 Field_Info 구조 제공
메서드
메서드 카운트
methods 테이블에 있는 method_info 구조의 수
메서드[카운트-1]
이 클래스나 인터페이스에 선언된 모든 메서드에 대한 Method_Info 제공
속성
속성 카운트
속성 테이블에 있는 속성의 수
속성[카운트-1]
속성 테이블의 각 값은 Attribue_Info 제공
매직 넘버(magic number)
The magic item supplies the magic number identifying the class file format; it has the value 0 xCAFEBABE.
매직 넘버 항목은 이 파일이 클래스 파일 형식임을 나타내는 매직 번호를 제공합니다.
값은0 xCAFEBABE으로 고정입니다.
클래스 파일 포맷 버전
The values of the minor_version and major_version items are the minor and major version numbers of this class file. Together, a major and a minor version number determine the version of the class file format. If a class file has major version number M and minor version number m, we denote the version of its class file format as M.m.
Thus, class file format versions may be ordered lexicographically, for example, 1.5 < 2.0 < 2.1. A Java Virtual Machine implementation can support a class file format of version v if and only if v lies in some contiguous range Mi.0 ≤ v ≤ Mj.m. The release level of the Java SE platform to which a Java Virtual Machine implementation conforms is responsible for determining the range. Oracle's Java Virtual Machine implementation in JDK release 1.0.2 supports class file format versions 45.0 through 45.3 inclusive. JDK releases 1.1.* support class file format versions in the range 45.0 through 45.65535 inclusive. For k ≥ 2, JDK release 1.k supports class file format versions in the range 45.0 through 44+k.0 inclusive.
클래스 파일의 메이저 및 마이너 버전을 나타냅니다. 이 정보는 클래스 파일 형식의 버전을 결정합니다.
클래스 파일의 주 버전 번호가 M이고, 부 버전이 m인 경우 클래스 파일 형식의 버전을 M.m으로 표시합니다.
실행하는 JVM(런타임 버전)이 컴파일한 JVM보다 버전이 낮거나, 호환이 되지 않는다면 런타임에 UnsupportedClassVersionError 예외가 발생합니다.
상수 풀
상수 풀 카운트
The value of the constant_pool_count item is equal to the number of entries in the constant_pool table plus one. A constant_pool index is considered valid if it is greater than zero and less than constant_pool_count, with the exception for constants of type long and double noted in §4.4.5.
상수 풀 카운트의 값은 상수 풀(constant_pool) 테이블의 항목 수에 1을 더한 값과 같습니다. (당연한 말입니다.)
상수 풀
The constant_pool is a table of structures (§4.4) representing various string constants, class and interface names, field names, and other constants that are referred to within the ClassFile structure and its substructures. The format of each constant_pool table entry is indicated by its first "tag" byte. 그림4. 상수 종류 The constant_pool table is indexed from 1 to constant_pool_count-1.
코드를 작성하면 정말 여러 곳곳에서 상수값이 등장합니다. (클래스명, 인터페이스명, 필드명... 그림4 참조) 각 constant_pool 테이블 항목의 형식은 첫 번째 "태그" 바이트로 표시됩니다. JVM은 코드를 실행할 때 런타임에 배치된 메모리 대신, 이 상수 풀 테이블을 찾아보고 필요한 값을 참조합니다.
The value of the access_flags item is a mask of flags used to denote access permissions to and properties of this class or interface. The interpretation of each flag, when set, is as shown in Table 4.1.
그림5. 액세스 플래그 A class may be marked with the ACC_SYNTHETIC flag to indicate that it was generated by a compiler and does not appear in source code. The ACC_ENUM flag indicates that this class or its superclass is declared as an enumerated type. An interface is distinguished by its ACC_INTERFACE flag being set. If its ACC_INTERFACE flag is not set, this class file defines a class, not an interface. If the ACC_INTERFACE flag of this class file is set, its ACC_ABSTRACT flag must also be set (JLS §9.1.1.1). Such a class file must not have its ACC_FINAL, ACC_SUPER or ACC_ENUM flags set. An annotation type must have its ACC_ANNOTATION flag set. If the ACC_ANNOTATION flag is set, the ACC_INTERFACE flag must be set as well. If the ACC_INTERFACE flag of this class file is not set, it may have any of the other flags in Table 4.1 set, except the ACC_ANNOTATION flag. However, such a class file cannot have both its ACC_FINAL and ACC_ABSTRACT flags set (JLS §8.1.1.2). The ACC_SUPER flag indicates which of two alternative semantics is to be expressed by the invokespecial instruction (§invokespecial) if it appears in this class. Compilers to the instruction set of the Java Virtual Machine should set the ACC_SUPER flag. The ACC_SUPER flag exists for backward compatibility with code compiled by older compilers for the Java programming language. In Oracle’s JDK prior to release 1.0.2, the compiler generated ClassFile access_flags in which the flag now representing ACC_SUPER had no assigned meaning, and Oracle's Java Virtual Machine implementation ignored the flag if it was set. All bits of the access_flags item not assigned in Table 4.1 are reserved for future use. They should be set to zero in generated class files and should be ignored by Java Virtual Machine implementations.
액세스 플래그는 클래스 또는 인터페이스에 대한 액세스 권한 및 속성을 나타내는 데 사용되는 값입니다.
각 플래그의 종류는 그림 5를 참고하시면 됩니다.
먼저 액세스 플래그의 구조로 보자면 크게 세 부분으로 나타나 있습니다.
플래그의 첫 부분은 일반 프로퍼티로, public 클래스인지, 상속이 금지된 Final 클래스인지를 나타냅니다.
두 번째로는 이 클래스 파일이 슈퍼클래스인지, 인터페이스인지, 추상 클래스인지를 나타냅니다.
플래그 끝 부분은 클래스 파일이 소스 코드에 없는 합성(SYNTHETIC) 클래스인지, 어노테이션(Annotation) 타입인지, 열거형(Enum) 타입인지를 표시합니다.
ACC_SYNTHETIC
클래스는 컴파일러에 의해 생성되었으며 소스 코드에 나타나지 않음을 나타내기 위해 ACC_SYNTHETIC 플래그로 표시될 수 있습니다.
ACC_INTERFACE
인터페이스라면 ACC_INTERFACE 플래그가 설정되어야 합니다.
ACC_INTERFACE가 설정되면 따라야 하는 규칙이 있습니다.
1. ACC_INTERFACE 플래그가 설정되면 ACC_ABSTRACT 플래그도 설정되어야 한다.
2. ACC_FINAL, ACC_SUPER 또는 ACC_ENUM 플래그가 설정되어서는 안 된다.
3. 주석 유형에는 ACC_ANNOTATION 플래그가 설정되어 있어야 합니다. ACC_ANNOTATION 플래그가 설정되면 ACC_INTERFACE 플래그도 설정되어야 한다.
만약 ACC_INTERFACE 플래그가 설정되지 않은 경우 ACC_ANNOTATION, ACC_FINAL, ACC_ABSTRACT 플래그를 제외하고 다른 플래그가 설정될 수 있습니다.
ACC_SUPER
ACC_SUPER 플래그는 슈퍼클래스인 경우에 설정되어야 하며, invokespecial 명령어가 표기됩니다.
이 플래그가 설정된 경우에는 ACC_FINAL 플래그가 설정되어서는 안 됩니다.
클래스
보통 클래스에 포함된 타입 계층을 나타내는 요소이며, 각각 상수 풀을 가리키는 인덱스로 표시됩니다.
This 클래스
The value of the this_class item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure (§4.4.1) representing the class or interface defined by this class file.
클래스에 포함된 this 타입 계층을 나타내는 요소입니다. This 클래스의 값은 상수 풀 테이블에 대한 유효한 인덱스여야 합니다. 해당 인덱스의 상수 풀 항목은 이 클래스 파일에 의해 정의된 클래스 또는 인터페이스를 나타내는 Constant_Class_Info 구조여야 합니다.
그림6. CONSTANT_Class_info 구조
Super 클래스
For a class, the value of the super_class item either must be zero or must be a valid index into the constant_pool table. If the value of the super_class item is nonzero, the constant_pool entry at that index must be a CONSTANT_Class_info structure (§4.4.1) representing the direct superclass of the class defined by this class file. Neither the direct superclass nor any of its superclasses may have the ACC_FINAL flag set in the access_flags item of its ClassFile structure. If the value of the super_class item is zero, then this class file must represent the class Object, the only class or interface without a direct superclass. For an interface, the value of the super_class item must always be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure representing the class Object.
클래스는 항상 이 항목의 값이 0(슈퍼 클래스 X)이거나, 상수 풀 테이블에 대한 유효한 인덱스여야 합니다.(슈퍼 클래스 O) 이 역시, 슈퍼 클래스를 나타내는 Constant_Class_Info 구조(그림 6 참고) 여야 합니다.
이 항목의 값이 0인 케이스는 두 가지로 나뉩니다.
1. 이 클래스가 직접적인 슈퍼클래스가 없는 유일한 클래스
2. 인터페이스인 Object 클래스를 나타내는 경우.
이 케이스의 경우 슈퍼 클래스 값은 항상 상수 풀 테이블에 대한 유효한 인덱스여야 합니다.
이때 해당 인덱스의 상수 풀 항목은 Object 클래스를 나타내야 합니다.
인터페이스
인터페이스 카운트
The value of the interfaces_count item gives the number of direct superinterfaces of this class or interface type.
해당 클래스 또는 인터페이스 유형의 직접적인 슈퍼 인터페이스 수를 나타냅니다.
인터페이스 테이블
The value of the interfaces_count item gives the number of direct superinterfaces of this class or interface type. interfaces [] Each value in the interfaces array must be a valid index into the constant_pool table. The constant_pool entry at each value of interfaces [i], where 0 ≤ i < interfaces_count, must be a CONSTANT_Class_info structure (§4.4.1) representing an interface that is a direct superinterface of this class or interface type, in the left-to-right order given in the source for the type.
인터페이스 배열의 각 값은 상수 풀 테이블에 대한 유효한 인덱스여야 합니다.
필드
필드 카운트
The value of the fields_count item gives the number of field_info structures in the fields table. The field_info structures (§4.5) represent all fields, both class variables and instance variables, declared by this class or interface type.
fields 테이블의 field_info 구조의 수를 제공합니다.(그림 6 참고) 이 클래스 또는 인터페이스 유형에 의해 선언된 모든 필드(클래스 변수 및 인스턴스 변수 모두)를 나타냅니다.
필드 테이블
Each value in the fields table must be a field_info (§4.5) structure giving a complete description of a field in this class or interface. The fields table includes only those fields that are declared by this class or interface. It does not include items representing fields that are inherited from superclasses or superinterfaces.
필드 테이블의 각 값은 이 클래스 또는 인터페이스의 필드에 대한 완전한 설명을 제공하는 Field_Info 구조를 띄어야 합니다.
필드 테이블에는 이 클래스 또는 인터페이스에 의해 선언된 필드만 포함됩니다. 슈퍼클래스나 슈퍼 인터페이스와 같이 상속된 필드를 나타내는 항목은 포함되지 않습니다.
그림7. Field_Info 구조
속성
속성 카운트
The value of the attributes_count item gives the number of attributes (§4.7) in the attributes table of this class.
이 클래스의 속성 테이블에 있는 속성의 수를 나타냅니다.
속성 테이블
Each value of the attributes table must be an attribute_info (§4.7) structure. 그림8. Attribue_Info 구조 The attributes defined by this specification as appearing in the attributes table of a ClassFile structure are the InnerClasses (§4.7.6), EnclosingMethod (§4.7.7), Synthetic (§4.7.8), Signature (§4.7.9), SourceFile (§4.7.10), SourceDebugExtension (§4.7.11), Deprecated (§4.7.15), RuntimeVisibleAnnotations (§4.7.16), RuntimeInvisibleAnnotations (§4.7.17), and BootstrapMethods (§4.7.21) attributes. 그림9. 속성 종류 If a Java Virtual Machine implementation recognizes class files whose version number is 49.0 or above, it must recognize and correctly read Signature (§4.7.9), RuntimeVisibleAnnotations (§4.7.16), and RuntimeInvisibleAnnotations (§4.7.17) attributes found in the attributes table of a ClassFile structure of a class file whose version number is 49.0 or above. If a Java Virtual Machine implementation recognizes class files whose version number is 51.0 or above, it must recognize and correctly read BootstrapMethods (§4.7.21) attributes found in the attributes table of a ClassFile structure of a class file whose version number is 51.0 or above. A Java Virtual Machine implementation is required to silently ignore any or all attributes in the attributes table of a ClassFile structure that it does not recognize. Attributes not defined in this specification are not allowed to affect the semantics of the class file, but only to provide additional descriptive information (§4.7.1).
우선적으로 다른 요소들처럼 속성 테이블의 각 값은 Attribue_Info 구조를 띄어야 합니다.
속성이 정의된 종류는 그림 9를 참고하시면 됩니다.
메서드
메서드 카운트
The value of the methods_count item gives the number of method_info structures in the methods table.
methods_count 항목의 값은 methods 테이블에 있는 method_info 구조의 수를 제공합니다.
그림10 Method_Info 구조
메서드 테이블
Each value in the methods table must be a method_info (§4.6) structure giving a complete description of a method in this class or interface. If neither of the ACC_NATIVE and ACC_ABSTRACT flags are set in the access_flags item of a method_info structure, the Java Virtual Machine instructions implementing the method are also supplied. 그림11 Method Flag The method_info structures represent all methods declared by this class or interface type, including instance methods, class methods, instance initialization methods (§2.9), and any class or interface initialization method (§2.9). The methods table does not include items representing methods that are inherited from superclasses or superinterfaces.
메서드 테이블의 각 값은 이 클래스 또는 인터페이스의 메서드에 대한 완전한 설명을 제공하는 Method_Info(그림 8 참고) 구조여야 합니다.method_info 구조의 access_flags 항목에 ACC_NATIVE 및 ACC_ABSTRACT 플래그가 모두 설정되지 않은 경우 해당 메서드를 구현하는 Java Virtual Machine 명령도 제공됩니다.
참고로 Method_Info 구조는 인스턴스 (초기화) 메서드, 클래스 메서드 및 모든 클래스 또는 인터페이스 초기화 메서드를 포함해 이 클래스 또는 인터페이스 유형에 의해 선언된 모든 메서드를 나타냅니다.
단 필드와 마찬가지로 상속된 메서드는 제외됩니다.
이제 HelloWorld.java를 컴파일 후 확인해 봅시다.
public class HelloWorld {
static final String str1 = "static final String1";
public static void main(String[] args) {
System.out.println();
System.out.println("Hello World");
method1();
}
public static void method1(){
String str = "var1";
System.out.println(str);
}
}
컴파일은 javac, 자바 바이트코드 확인은 javap를 이용하시고 더 자세한 정보를 원하면 -v 옵션을 주시면 됩니다.