programing

Java 클래스 파일 작성은 결정적입니까?

sourcejob 2022. 9. 30. 11:05
반응형

Java 클래스 파일 작성은 결정적입니까?

동일한 JDK를 사용하는 경우(즉, 동일)javacexecutive 파일)은 항상 동일한 클래스 파일입니까?운영체제하드웨어에 따라 차이가 있습니까?JDK 버전 외에 다른 점이 있습니까?차이를 피하기 위한 컴파일러 옵션이 있습니까?이론적으로만 차이가 나는가, 아니면 Oracle의javac실제로 동일한 입력 옵션과 컴파일러 옵션에 대해 다른 클래스 파일을 생성합니까?

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

업데이트 2 'Same JDK'는 같은 의미이기도 합니다.javac실행 가능.

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

[편집, 바꿔치기 질문 추가]
"같은 javac 실행 파일이 다른 플랫폼에서 실행될 때 다른 바이트 코드가 생성되는 상황은 무엇입니까?"

이렇게 생각해 봅시다.

완전히 호환되는 자바 컴파일러를 쉽게 만들 수 있지만, 결코 동일한 컴파일러를 만들 수 없습니다..class같은 파일을 2회 제출합니다..java파일.

모든 종류의 바이트 코드 구성을 조정하거나 불필요한 속성을 메서드에 추가하는 것만으로 이를 수행할 수 있습니다(이는 허용됩니다.

사양에서는 컴파일러가 바이트 대 바이트의 동일한 클래스 파일을 생성할 필요가 없기 때문에 이러한 결과에 의존하지 않도록 하겠습니다.

다만, 몇 번인가 확인한 결과, 같은 컴파일러로 같은 소스 파일을 같은 스위치(및 같은 라이브러리!)로 컴파일 하면, 같은 결과가 됩니다..class파일을 표시합니다.

업데이트: 최근 Java 7에서의 on 구현에 관한 흥미로운 블로그 투고를 접했습니다.이 블로그 투고에서는, 여기(강조)에서 인용하는 몇개의 관련 부분이 있습니다.

컴파일러의 출력을 예측 가능하고 반복 가능하게 하기 위해, 이러한 데이터 구조에서 사용되는 맵과 집합은 다음과 같습니다.LinkedHashMapLinkedHashSet단순한 것이 아니라HashMaps그리고.HashSets특정 컴파일 중에 생성된 코드의 기능적 정확성에 관해서사용해도 문제 없습니다.반복 순서는 문제가 되지 않습니다.단, 시스템 클래스의 구현 세부 사항에 따라출력이 달라지지 않도록 하는 것이 유익하다고 생각합니다.

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

컴파일러가 각 플랫폼에서 동일한 바이트 코드를 생성할 의무는 없습니다.다른 벤더에 문의해 주십시오.javac유틸리티를 사용하여 특정 답변을 얻을 수 있습니다.


파일 오더에 대한 실용적인 예를 제시하겠습니다.

2개의 jar 파일이 있다고 칩시다.my1.jar그리고.My2.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)입니다.

Java 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

minor_version 및 major_version 항목의 값은 이 클래스 파일의 마이너 및 메이저버전 번호입니다.메이저 버전 번호와 마이너버전 번호가 함께 클래스 파일 형식의 버전을 결정합니다.클래스 파일에 메이저버전 번호 M과 마이너버전 번호 m이 있는 경우 클래스 파일 포맷의 버전을 M.m으로 나타냅니다.따라서 클래스 파일 형식의 버전은 사전 편집순으로 정렬할 수 있습니다(예: 1.5 < 2.0 < 2.1).Java 가상 시스템 구현은 v가 Mi.0 v Mj.m의 연속된 범위에 있는 경우에만 버전 v의 클래스 파일 형식을 지원할 수 있습니다.Java 플랫폼의 특정 릴리스 레벨에 준거한 Java 가상 머신의 구현이 지원하는 버전의 범위를 지정할 수 있는 것은 Sun뿐입니다.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와 관계 없이 생성된 바이트 코드는 항상 동일하다고 생각합니다.바이트 코드 생성은 자바 컴파일러에 의해 이루어지며, 자바 컴파일러는 소스 코드를 바이트 코드로 "변환"하는 결정론적 알고리즘을 사용합니다.따라서 출력은 항상 동일합니다.이러한 상황에서는 소스 코드의 갱신만이 출력에 영향을 줍니다.

전체적으로 동일한 컴파일러에 의해 컴파일되지만 다른 플랫폼에서 컴파일될 때 동일한 소스가 동일한 바이트 코드를 생성한다는 보장은 없습니다.

다양한 언어(코드 페이지)에 관한 시나리오(일본어 지원 Windows 등)에 대해 알아보겠습니다.멀티바이트 문자를 생각해 보십시오.컴파일러가 항상 모든 언어를 지원해야 한다고 가정하지 않으면 8비트 ASCII에 최적화될 수 있습니다.

Java Language Specification에는 바이너리 호환성에 대한 섹션이 있습니다.

Java 프로그래밍 언어의 바이너리 바이너리 바이너리 호환성은 SOM(Forman, Conner, Danforth 및 Raper, Proceedings of OPSLA'95)의 프레임워크 내에서 작성자가 특정하는 모든 관련 변환(인스턴스 변수 추가에 관한 몇 가지 주의사항 포함)에서 바이너리 호환성이 있습니다.Java 프로그래밍 언어가 지원하는 중요한 바이너리 호환 변경의 목록을 다음에 나타냅니다.

•기존 메서드, 컨스트럭터, 이니셜라이저를 재실행하여 성능을 향상시킨다.

• 메서드 또는 컨스트럭터를 변경하여 이전에는 발생해서는 안 되는 예외를 발생시키거나 무한 루프에 들어가거나 교착 상태를 일으켜 실패한 입력 값을 반환한다.

•기존 클래스 또는 인터페이스에 새로운 필드, 메서드 또는 컨스트럭터 추가

•클래스의 개인 필드, 메서드 또는 생성자 삭제

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

•기존 유형 선언의 필드, 메서드 또는 생성자 순서 변경

•클래스 계층에서 메서드를 위쪽으로 이동시킨다.

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

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

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

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

편집

기술적으로 같은 코멘트가 의미하는 것은 그것이다.바이트를 바이트 단위로 비교할 경우 완전히 같을 필요는 없습니다.

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

질문의 경우:

"같은 javac 실행 파일이 다른 플랫폼에서 실행될 때 다른 바이트 코드가 생성되는 상황은 무엇입니까?"

크로스 컴파일 예에서는 Javac 옵션:-target version을 사용하는 방법을 보여 줍니다.

이 플래그는 이 명령어를 호출할 때 지정한 Java 버전과 호환되는 클래스 파일을 생성합니다.따라서 클래스 파일은 이 옵션을 사용하여 컴파일할 때 제공하는 속성에 따라 달라집니다.

대부분의 경우 대답은 "예"이지만 정확한 답변을 얻으려면 컴파일 중에 몇 가지 키 또는 GUID 생성을 검색해야 합니다.

나는 이 일이 일어난 상황을 기억할 수 없다.예를 들어, 시리얼라이즈를 목적으로 한 ID는, 프로그래머나 IDE에 의해서 생성되는 하드 코드화 됩니다.

추신: JNI도 중요할 수 있습니다.

P.P.S.가 건javac자바어하다는 것을 합니다.따라서 이유 없이 다른 코드를 생성하지 않습니다.이를 실행할 수 있는 것은 네이티브콜뿐이에요

두 가지 질문이 있습니다.

Can there be a difference depending on the operating system or hardware? 

이건 이론적인 질문이고 답은 분명히 있을 수 있다입니다. 있을 수 있습니다.다른 사람들이 말했듯이, 이 사양은 컴파일러가 바이트 대 바이트의 동일한 클래스 파일을 생성하도록 요구하지 않습니다.

현재 존재하는 모든 컴파일러가 모든 상황에서 동일한 바이트 코드를 생성하더라도(다른 하드웨어 등), 내일의 답은 다를 수 있습니다.javac 또는 운영체제를 업데이트하지 않을 경우 특정 상황에서 해당 버전의 동작을 테스트할 수 있지만, 예를 들어 Java 7 Update 11에서 Java 7 Update 15로 변경하면 결과가 달라질 수 있습니다.

What are the circumstances where the same javac executable, when run on a different platform, will produce different bytecode?

그건 알 수 없어요.

구성 관리가 질문을 하는 이유인지는 모르겠지만, 신경 써야 하는 이유는 이해할 수 있습니다.바이트 코드 비교는 정당한 IT 제어이지만 클래스 파일의 변경 여부를 판단하기 위한 것일 뿐 소스 파일의 변경 여부를 가장 먼저 판단하지는 않습니다.

다른 말로 하자면.

첫째, 이 질문은 결정론적인 것이 아니라고 생각합니다.

물론 그것은 결정론적이다: 무작위성은 컴퓨터 과학에서 달성하기 어렵고, 컴파일러가 어떤 이유로든 여기에 그것을 도입할 이유가 없다.

둘째, "같은 소스 코드 파일에 대해 바이트 코드 파일이 얼마나 유사합니까?"로 재구성하면 아니, 유사하다는 사실에 의존할 없습니다.

이것을 확인하는 좋은 방법은 git 스테이지에 .class(내 경우는 .pyc)를 그대로 두는 것입니다.팀 내 다른 컴퓨터들 사이에서 .py 파일(및 .pyc 재컴파일)이 변경되지 않았을 때 git이 .pyc 파일 간에 변경되었음을 알게 될 것입니다.

적어도 나는 그것을 관찰했다.*.pyc와 *.class를 .gitignore에 넣으세요!

언급URL : https://stackoverflow.com/questions/14984984/is-the-creation-of-java-class-files-deterministic

반응형