나는 C ++에서 정의되지 않은 일반적인 동작을 논의 하는 SO에 대한이 질문을 읽었 으며 Java에 정의되지 않은 동작이 있습니까?
이 경우 Java에서 정의되지 않은 동작의 일반적인 원인은 무엇입니까?
그렇지 않은 경우 Java의 어떤 기능으로 인해 그러한 동작이 발생하지 않으며 왜 최신 버전의 C 및 C ++가 이러한 특성으로 구현되지 않았습니까?
나는 C ++에서 정의되지 않은 일반적인 동작을 논의 하는 SO에 대한이 질문을 읽었 으며 Java에 정의되지 않은 동작이 있습니까?
이 경우 Java에서 정의되지 않은 동작의 일반적인 원인은 무엇입니까?
그렇지 않은 경우 Java의 어떤 기능으로 인해 그러한 동작이 발생하지 않으며 왜 최신 버전의 C 및 C ++가 이러한 특성으로 구현되지 않았습니까?
답변:
Java에서는 잘못 동기화 된 프로그램의 동작이 정의되지 않은 것으로 간주 할 수 있습니다.
Java 7 JLS는 17.4.8 에서 "정의되지 않은"단어를 한 번 사용합니다 . 실행 및 인과 관계 요구 사항 :
f|d
의 도메인을f
로 제한하여 제공된 함수를 나타내는 데 사용 합니다d
. 모두x
의d
,f|d(x) = f(x)
모두를위한, 그리고x
에 없습니다d
,f|d(x)
되고 정의되지 않은 ...
Java API 설명서는 결과가 정의되지 않은 일부 사례를 지정합니다 (예 : (더 이상 사용되지 않는) 생성자 Date (int year, int month, int day) :
주어진 인수가 범위를 벗어난 경우 결과는 정의되지 않습니다 ...
ExecutorService.invokeAll (Collection) 상태에 대한 Javadoc :
이 작업이 진행되는 동안 지정된 컬렉션을 수정하면 이 메서드의 결과가 정의되지 않습니다 ...
API 문서에서 "최선의 노력"이라는 용어를 사용하는 ConcurrentModificationException 과 같이 덜 정의 된 "정의되지 않은"동작은 다음과 같습니다.
일반적으로 말해서 비동기식 동시 수정이있을 경우 확실한 보장을 할 수 없으므로 페일-패스트 동작을 보장 할 수 없습니다. 빠른 작업
ConcurrentModificationException
은 최선의 노력 으로 이루어집니다. 따라서이 예외에 의존하는 프로그램을 작성하는 것은 잘못 될 것입니다 ...
질문 의견 중 하나는 Eric Lippert의 기사를 참조하여 주제 문제에 대한 유용한 소개를 제공합니다. 구현 정의 동작 .
필자는 저자가 Java가 아닌 C #을 대상으로한다는 점을 명심해야 할지라도 언어 독립적 추론을 위해이 기사를 추천한다.
전통적으로 우리는 프로그래밍 언어 관용구가 관용구의 사용이 어떤 영향을 미칠 수 있다면 정의되지 않은 행동 을한다고 말합니다. 예상대로 작동하거나 하드 디스크를 지우거나 시스템을 중단시킬 수 있습니다. 또한 컴파일러 작성자는 정의되지 않은 동작에 대해 경고 할 의무가 없습니다. (사실, 언어 정의에 의해 "정의되지 않은 동작"관용구를 사용하는 프로그램이 컴파일러와 충돌하도록 허용되는 일부 언어가 있습니다!) ...
반대로 구현 정의 동작 이 있는 관용구 는 컴파일러 작성자가 기능을 구현하는 방법에 대한 몇 가지 선택 사항을 갖고 선택해야하는 동작입니다. 이름에서 알 수 있듯이 구현 정의 동작은 최소한 정의되어 있습니다. 예를 들어 C #을 사용하면 정수 나누기가 오버플로 될 때 구현에서 예외를 발생 시키거나 값을 생성 할 수 있지만 구현시 하나를 선택해야합니다. 하드 디스크를 지울 수 없습니다 ...
언어 설계위원회가 특정 언어 관용구를 정의되지 않은 또는 구현 정의 된 동작으로 남겨 두도록하는 요인은 무엇입니까?
첫 번째 주요 요인은 시장에 특정 프로그램의 행동에 동의하지 않는 기존의 두 가지 언어 구현이 있습니까? ...
다음으로 중요한 요소는 이 기능이 자연스럽게 구현할 수있는 여러 가지 가능성을 제시 하는가? ...
세 번째 요소는 다음과 같습니다 . 기능이 너무 복잡하여 정확한 동작을 자세히 분석하기 어렵거나 비용이 많이 듭니까? ...
네 번째 요소는 이 기능이 컴파일러가 분석해야하는 부담이 큰가? ...
다섯 번째 요소는 이 기능이 런타임 환경에 큰 부담을 주는가? ...
여섯 번째 요소는 다음과 같습니다. 동작을 정의하면 주요 최적화가 불가능합니까? ...
그것들은 생각 나는 몇 가지 요소 일뿐입니다. 물론 "구현 정의"또는 "미정의"기능을 만들기 전에 언어 설계위원회가 토론하는 많은 다른 요소가 있습니다.
위의 내용은 매우 간단한 내용입니다. 전체 기사에는이 발췌 부분에서 언급 한 요점에 대한 설명과 예가 들어 있습니다. 그것은이다 훨씬 가치가 독서. 예를 들어, "6 번째 요소"에 대한 세부 사항은 Java 메모리 모델 ( JSR 133 )의 많은 명령문에 대한 동기 부여에 대한 통찰력을 제공하여 일부 최적화가 허용되는 이유를 이해하고 정의되지 않은 동작을 유발하는 반면 다른 것들은 금지되어 있습니다. 발생 및 인과 관계 요구 사항 과 같은 제한 사항 .
기사 자료 중 어느 것도 나에게 새로운 것이 아니지만 그처럼 우아하고 간결하며 이해할 수있는 방식으로 제시 된 것을 본다면 저주받을 것입니다. 놀랄 만한.
내 머리 위로, 적어도 C ++에서와 같은 의미로 Java에는 정의되지 않은 동작이 있다고 생각하지 않습니다.
그 이유는 C ++과는 다른 Java의 철학이 있기 때문입니다. Java의 핵심 디자인 목표는 프로그램이 여러 플랫폼에서 변경없이 실행될 수 있도록하는 것이 었습니다. 따라서 사양은 모든 것을 매우 명시 적으로 정의합니다.
반대로 C 및 C ++의 핵심 설계 목표는 효율성입니다. 필요하지 않더라도 성능을 저하시키는 기능 (플랫폼 독립 포함)이 없어야합니다. 이를 위해, 스펙은 일부 플랫폼에서 추가 작업을 야기 할 수 있으므로 특정 플랫폼에 대해 프로그램을 작성하고 모든 고유 한 특성을 알고있는 사람들의 경우에도 성능을 저하 시키므로 의도적으로 일부 동작을 정의하지 않습니다.
Java가 그 이유에 대해 정의되지 않은 동작의 제한된 형태를 소급해서 도입 한 예가 있습니다. strictfp 키워드는 스펙이 이전에 요구 한대로 IEEE 754 표준을 따르지 않고 부동 소수점 계산을 벗어날 수 있도록 Java 1.2에 도입되었습니다. 따라서 추가 작업이 필요하고 일부 공통 CPU에서 모든 부동 소수점 계산이 느려졌지만 실제로는 경우에 따라 더 나쁜 결과가 생성되기 때문입니다.
int x=-1; foo(); x<<=1;
초현대적 인 철학은 재 작성을 선호 foo
하므로 빠져 나가지 않는 경로는 도달 할 수 없어야합니다. 경우에, foo
이다 if (should_launch_missiles) { launch_missiles(); exit(1); }
컴파일러 (그리고 어떤 사람들에 따른한다) 단순히 그것을 단순화 수 있습니다 launch_missiles(); exit(1);
. 기존의 UB는 임의 코드 실행 이었지만 시간과 인과 관계에 의해 제한되었습니다. 새로운 개선 된 UB는 어느 쪽에도 구속되지 않습니다.
Java는 이전 언어의 교훈으로 인해 정의되지 않은 행동을 근절하기 위해 열심히 노력합니다. 예를 들어 클래스 수준 변수는 자동으로 초기화됩니다. 로컬 변수는 성능상의 이유로 자동 초기화되지 않지만,이를 감지 할 수있는 프로그램을 작성하지 못하게하는 정교한 데이터 흐름 분석이 있습니다. 참조는 포인터가 아니므로 유효하지 않은 참조가 존재할 수 없으며 역 참조로 null
인해 특정 예외가 발생합니다.
물론 완전히 지정되지 않은 일부 동작이 남아 있으며 신뢰할 수없는 프로그램을 가정 할 수 있습니다. 예를 들어, 일반 (정렬되지 않은) 반복하는 Set
경우 언어는 각 요소를 한 번만 볼 수 있지만 어떤 순서로 보지는 보장하지 않습니다. 연속 실행에서 순서가 동일하거나 변경 될 수 있습니다. 또는 다른 할당이 발생하지 않거나 JDK 등을 업데이트하지 않는 한 동일하게 유지 될 수 있습니다 . 이러한 모든 효과를 없애는 것은 거의 불가능합니다 . 예를 들어, 모든 Collections 작업 을 명시 적으로 주문하거나 무작위 화 해야 하며, 이는 정의되지 않은 작은 추가 가치가 없습니다.
"정의되지 않은 행동"과 그 기원을 이해해야합니다.
정의되지 않은 동작 은 표준에 의해 정의 되지 않은 동작을 의미합니다. C / C ++의 컴파일러 구현 및 추가 기능이 너무 많습니다. 이러한 추가 기능은 코드를 컴파일러에 연결했습니다. 중앙 집중식 언어 개발이 없었기 때문입니다. 따라서 일부 컴파일러의 고급 기능 중 일부는 "정의되지 않은 동작"이되었습니다.
Java에서 언어 사양은 Sun-Oracle에 의해 제어되며 다른 사람은 사양을 만들려고하지 않으므로 정의되지 않은 동작이 없습니다.
편집은 특히 질문에 답변
Java는 C / C ++에있는 정의되지 않은 모든 동작을 근본적으로 제거합니다. (예 : 부호있는 정수 오버플로, 0으로 나누기, 초기화되지 않은 변수, 널 포인터 역 참조, 비트 폭 이상으로 이동, 이중 프리, "소스 코드 끝에 줄 바꿈 없음"). 프로그래머가 거의 접하지 않습니다.
Java가 C 또는 C ++ 코드를 호출하는 방법 인 JNI (Java Native Interface) 함수 서명을 잘못 받거나, JVM 서비스를 잘못 호출하거나, 메모리를 손상시키고, 물건을 잘못 할당 / 해제하는 등 JNI를 망치는 방법에는 여러 가지가 있습니다. 이전에 이러한 실수를 저지른 적이 있으며 일반적으로 JNI 코드를 실행하는 하나의 스레드에서 오류가 발생하면 전체 JVM이 충돌합니다.
Thread.stop()
더 이상 사용되지 않습니다. 인용문:
Thread.stop
더 이상 사용되지 않는 이유는 무엇 입니까?본질적으로 안전하지 않기 때문입니다. 스레드를 중지하면 스레드가 잠근 모든 모니터의 잠금이 해제됩니다. (
ThreadDeath
예외가 스택을 전파 함에 따라 모니터가 잠금 해제됩니다 .) 이전에 이러한 모니터로 보호 된 오브젝트 중 하나가 일관성이없는 상태 인 경우 다른 스레드가이 오브젝트를 일치하지 않는 상태로 볼 수 있습니다. 이러한 물체는 손상되었다고합니다. 스레드가 손상된 객체에서 작동하면 임의의 동작이 발생할 수 있습니다. 이 동작은 미묘하고 감지하기 어려울 수 있습니다. 확인되지 않은 다른 예외와 달리ThreadDeath
스레드를 자동으로 종료합니다. 따라서 사용자는 자신의 프로그램이 손상되었다는 경고를받지 않습니다. 실제 손상이 발생한 후 언제라도 몇 시간 또는 며칠이라도 손상이 발생할 수 있습니다.
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html