[java] Java 클래스 파일 생성이 결정적입니까?

사용하는 경우 동일한 JDK (즉, 같은 javac실행 파일을), 생성 된 클래스 파일이 항상 동일하다? 운영 체제 또는 하드웨어 에 따라 차이가있을 수 있습니까? JDK 버전을 제외하고 다른 요인으로 인해 차이가 발생할 수 있습니까? 차이점을 피하기위한 컴파일러 옵션이 있습니까? 이론상으로 만 차이가 있거나 Oracle의javac 실제로 동일한 입력 및 컴파일러 옵션에 대해 다른 클래스 파일을 생성합니까?

업데이트 1 클래스 파일을 실행할 수 있는지 여부가 아닌 생성 , 즉 컴파일러 출력에 관심 이 있습니다. 다양한 플랫폼에서 있습니다.

업데이트 2 ‘Same JDK’로 나도 같은 의미javac 실행 파일을 .

업데이트 3 Oracle 컴파일러의 이론적 차이와 실제 차이의 구별.

[편집, 의역 된 질문 추가]
“다른 플랫폼에서 실행할 때 동일한 javac 실행 파일이 다른 바이트 코드를 생성하는 상황은 무엇입니까?”



답변

다음과 같이합시다.

동일한 .class파일이 주어지면 동일한 파일을 두 번 생성하지 않는 완전히 일치하는 Java 컴파일러를 쉽게 생성 할 수 있습니다 .java.

모든 종류의 바이트 코드 구성을 조정하거나 단순히 내 메서드에 불필요한 속성을 추가하여이를 수행 할 수 있습니다 (허용됨).

사양 이 컴파일러가 바이트 단위 동일한 클래스 파일을 생성 하도록 요구 하지 않는다는 점을 감안할 때 이러한 결과 에 의존 하는 것을 피할 것 입니다.

그러나 몇 번 확인한 결과 동일한 스위치 (및 동일한 라이브러리!)를 사용하여 동일한 컴파일러로 동일한 소스 파일을 컴파일하면 동일한 파일 생성되었습니다 .class.

업데이트 : 최근 에 Java 7에서 on 구현에 대한 흥미로운 블로그 게시물을switchString 우연히 발견했습니다 . 이 블로그 게시물에는 여기에서 인용 할 관련 부분이 있습니다 (강조 표시).

컴파일러의 출력은 예측 가능하고 반복하기 위해, 이러한 데이터 구조에 사용 된지도와 세트는 LinkedHashMaps와 LinkedHashSet단지보다는이야 HashMaps하고 HashSets. 생성 된 코드의 기능 정확성의 측면에서 , 주어진 컴파일시에 사용 HashMap하고 HashSet잘 될 것입니다 ; 반복 순서는 중요하지 않습니다. 그러나 시스템 클래스의 구현 세부 사항에 따라의 출력이 달라지지 않는 것이 유익하다는 것을 알게되었습니다javac .

이것은 문제를 매우 명확하게 보여줍니다. 컴파일러는 사양과 일치하는 한 결정적인 방식으로 작동 할 필요없습니다 . 그러나 컴파일러 개발자는 일반적으로 시도 하는 것이 좋습니다 (너무 비싸지 않은 경우).


답변

컴파일러가 각 플랫폼에서 동일한 바이트 코드를 생성 할 의무는 없습니다. javac특정 답변을 얻으려면 다른 공급 업체의 유틸리티에 문의해야합니다 .


파일 순서를 지정하여 이에 대한 실제 예를 보여 드리겠습니다.

2 개의 jar 파일이 있다고 가정 해 봅시다 : my1.jarMy2.jar. 그들은 lib나란히 디렉토리에 저장됩니다. 컴파일러는 (이 때문에 알파벳 순서로 읽고 lib)하지만, 순서는 my1.jar, My2.jar파일 시스템의 경우를 구분하는 경우, 그리고 My2.jar,my1.jar 그것은 대소 문자를 구분합니다.

my1.jar클래스를 가지고 A.class하는 방법으로

public class A {
     public static void a(String s) {}
}

My2.jar동일한 갖고 A.class있지만, 다른 방법 서명 (수락 Object)

public class A {
     public static void a(Object o) {}
}

전화가 오면

String s = "x";
A.a(s); 

다른 경우에 다른 서명으로 메서드 호출을 컴파일합니다. 따라서 파일 시스템 대소 문자 구분에 따라 결과적으로 다른 클래스를 얻게됩니다.


답변

짧은 답변- 아니요


긴 답변

그들은 bytecode다른 플랫폼에 대해 동일 할 필요는 없다. 바이트 코드를 정확히 실행하는 방법을 아는 것은 JRE (Java Runtime Environment)입니다.

당신은을 통해 이동하면 자바 VM 사양 이이 바이트 코드가 다른 플랫폼에서 동일 함을 사실 필요가 없다 것을 알게 될 것입니다.

를 통과 클래스 파일 형식 , 그것은 클래스 파일의 구조 등을 보여줍니다

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

부 버전 및 주 버전 확인

부 버전, 주 버전

minor_version 및 major_version 항목의 값은이 클래스 파일의 부 및 주 버전 번호이며 주 및 부 버전 번호가 함께 클래스 파일 형식의 버전을 결정합니다. 클래스 파일에 주 버전 번호 M과 부 버전 번호 m이있는 경우 해당 클래스 파일 형식의 버전을 Mm으로 표시합니다. 따라서 클래스 파일 형식 버전은 사전 순으로 정렬 될 수 있습니다 (예 : 1.5 <2.0 <2.1). Java 가상 머신 구현은 v가 연속적인 범위 Mi.0 v Mj.m에있는 경우에만 버전 v의 클래스 파일 형식을 지원할 수 있습니다. Sun만이 Java 플랫폼의 특정 릴리스 레벨을 준수하는 Java 가상 머신 구현이 지원할 수있는 버전의 범위를 지정할 수 있습니다 .1

각주를 통해 더 읽기

1 Sun의 JDK 릴리스 1.0.2의 Java 가상 머신 구현은 클래스 파일 형식 버전 45.0에서 45.3까지를 지원합니다. Sun의 JDK 릴리스 1.1.X는 45.0에서 45.65535까지 범위의 클래스 파일 형식을 지원할 수 있습니다. Java 2 플랫폼 버전 1.2의 구현은 45.0에서 46.0까지의 범위에있는 버전의 클래스 파일 형식을 지원할 수 있습니다.

따라서이 모든 것을 조사하면 서로 다른 플랫폼에서 생성 된 클래스 파일이 동일 할 필요가 없음을 알 수 있습니다.


답변

첫째, 사양에는 그러한 보장이 전혀 없습니다. 준수 컴파일러는 컴파일 시간을 생성 된 클래스 파일에 추가 (사용자 지정) 속성으로 표시 할 수 있으며 클래스 파일은 여전히 ​​정확합니다. 그러나 모든 단일 빌드에서 바이트 수준의 다른 파일을 생성하며 사소한 일입니다.

두 번째로, 그런 성가신 트릭이 없더라도 컴파일러가 구성과 입력이 두 경우 모두 동일하지 않는 한 연속으로 두 번 정확히 동일한 작업을 수행 할 것으로 기대할 이유가 없습니다. 사양 소스 파일 이름을 표준 속성 중 하나로 설명하며 소스 파일에 빈 줄을 추가하면 줄 번호 테이블을 변경할 수 있습니다.

셋째, 호스트 플랫폼 (클래스 경로에있는 내용의 차이로 인한 것 제외)으로 인해 빌드에서 차이가 발생하지 않았습니다. 플랫폼 (즉, 네이티브 코드 라이브러리)에 따라 달라지는 코드는 클래스 파일의 일부가 아니며 바이트 코드에서 네이티브 코드의 실제 생성은 클래스가로드 된 후에 발생합니다.

넷째로 (그리고 가장 중요한 것은) 이것을 알고 싶어하는 것은 나쁜 프로세스 냄새 (코드 냄새와 같지만 코드에서 어떻게 행동하는지에 대한) 냄새가납니다. 빌드가 아닌 가능한 경우 소스 버전을 지정하고 빌드 버전을 지정해야하는 경우 개별 클래스 파일이 아닌 전체 구성 요소 수준에서 버전을 지정합니다. 우선적으로 CI 서버 (예 : Jenkins)를 사용하여 소스를 실행 가능한 코드로 전환하는 프로세스를 관리합니다.


답변

동일한 JDK를 사용하면 생성 된 바이트 코드는 사용되는 하드웨어 및 OS와 관계없이 항상 동일 할 것이라고 믿습니다. 바이트 코드 생성은 결정적 알고리즘을 사용하여 소스 코드를 바이트 코드로 “변환”하는 Java 컴파일러에 의해 수행됩니다. 따라서 출력은 항상 동일합니다. 이러한 조건에서는 소스 코드에 대한 업데이트 만 출력에 영향을줍니다.


답변

전반적으로 동일한 컴파일러가 다른 플랫폼에서 컴파일 할 때 동일한 소스가 동일한 바이트 코드를 생성 할 것이라는 보장은 없습니다.

예를 들어 일본어를 지원하는 Windows와 같은 다른 언어 (코드 페이지)와 관련된 시나리오를 살펴 보겠습니다. 다중 바이트 문자를 생각하십시오. 컴파일러가 항상 모든 언어를 지원해야한다고 가정하지 않는 한 8 비트 ASCII에 대해 최적화 할 수 있습니다.

Java 언어 사양 에 바이너리 호환성에 대한 섹션이 있습니다 .

SOM (Forman, Conner, Danforth 및 Raper, Proceedings of OOPSLA ’95)의 Release-to-Release 바이너리 호환성 프레임 워크 내에서 Java 프로그래밍 언어 바이너리는 작성자가 식별하는 모든 관련 변환에서 바이너리 호환이 가능합니다 (일부 경고 포함). 인스턴스 변수 추가 관련). 이 체계를 사용하여 다음은 Java 프로그래밍 언어가 지원하는 몇 가지 중요한 바이너리 호환 변경 목록입니다.

• 성능 향상을 위해 기존 메서드, 생성자 및 이니셜 라이저를 다시 구현합니다.

• 이전에 일반적으로 발생해서는 안되는 예외를 던 졌거나 무한 루프에 들어가거나 교착 상태를 유발하여 실패한 입력에 대한 값을 반환하도록 메서드 또는 생성자를 변경합니다.

• 기존 클래스 또는 인터페이스에 새 필드, 메서드 또는 생성자를 추가합니다.

• 클래스의 개인 필드, 메서드 또는 생성자를 삭제합니다.

• 전체 패키지가 업데이트되면 기본 (패키지 전용) 액세스 필드, 메서드 또는 패키지의 클래스 및 인터페이스 생성자를 삭제합니다.

• 기존 유형 선언에서 필드, 메서드 또는 생성자를 재정렬합니다.

• 클래스 계층 구조에서 메서드를 위로 이동.

• 클래스 또는 인터페이스의 직접 수퍼 인터페이스 목록 재정렬.

• 유형 계층 구조에 새 클래스 또는 인터페이스 유형을 삽입합니다.

이 장에서는 모든 구현에서 보장되는 바이너리 호환성에 대한 최소 표준을 지정합니다. Java 프로그래밍 언어는 호환 가능한 소스에서 가져온 것으로 알려지지 않았지만 여기에 설명 된 호환 가능한 방식으로 소스가 수정 된 클래스 및 인터페이스의 바이너리가 혼합 될 때 호환성을 보장합니다. 응용 프로그램 릴리스 간의 호환성에 대해 논의하고 있습니다. Java SE 플랫폼 릴리스 간의 호환성에 대한 논의는이 장의 범위를 벗어납니다.


답변

Java allows you write/compile code on one platform and run on different platform.
AFAIK ; 이것은 다른 플랫폼에서 생성 된 클래스 파일이 동일하거나 기술적으로 동일 할 때만 가능합니다.

편집하다

기술적으로 동일한 의견이 의미하는 바 는 그 것입니다. 바이트 단위로 비교하는 경우 정확히 동일 할 필요는 없습니다.

따라서 사양에 따라 다른 플랫폼에있는 클래스의 .class 파일은 바이트 단위로 일치 할 필요가 없습니다.