私は、SOでこの質問を読んでいて、C ++の一般的な未定義の動作について説明していましたが、Javaにも未定義の動作がありますか?
その場合、Javaの未定義の動作の一般的な原因は何ですか?
そうでない場合、Javaのどの機能がそのような動作から解放されますか?これらのプロパティでCおよびC ++の最新バージョンが実装されていないのはなぜですか?
私は、SOでこの質問を読んでいて、C ++の一般的な未定義の動作について説明していましたが、Javaにも未定義の動作がありますか?
その場合、Javaの未定義の動作の一般的な原因は何ですか?
そうでない場合、Javaのどの機能がそのような動作から解放されますか?これらのプロパティでCおよびC ++の最新バージョンが実装されていないのはなぜですか?
回答:
Javaでは、誤って同期されたプログラムの動作が未定義であることを考慮することができます。
Java 7 JLSは、17.4.8で「未定義」という単語を1回使用します。実行と因果関係の要件:
私たちは、使用
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
は、ベストエフォートベースでスローされます。したがって、その正確性をこの例外に依存するプログラムを書くことは間違っているでしょう...
質問のコメントの 1つは、Eric Lippertによるトピックの問題への有用な紹介を提供する記事、実装定義の動作を参照しています。
著者はJavaではなくC#を対象とすることを心に留めておく必要がありますが、言語にとらわれない理由でこの記事をお勧めします。
伝統的に、プログラミング言語のイディオムは、そのイディオムの使用がなんらかの効果をもたらす可能性がある場合、未定義の動作を持つと言います。期待どおりに動作したり、ハードディスクを消去したり、マシンをクラッシュさせる可能性があります。さらに、コンパイラの作成者は、未定義の動作について警告する義務を負いません。(そして実際には、「未定義の動作」イディオムを使用するプログラムが言語仕様によってコンパイラをクラッシュさせることが許可されている言語があります!)...
対照的に、実装定義の動作を持つイディオムは、コンパイラの作成者が機能の実装方法についていくつかの選択肢を持ち、そのうちの1つを選択する必要がある動作です。名前が示すように、実装定義の動作は少なくとも定義されています。たとえば、C#では、整数除算がオーバーフローしたときに、実装で例外をスローしたり値を生成したりできますが、実装では1つを選択する必要があります。ハードディスクを消去できません...
言語設計委員会が特定の言語イディオムを未定義または実装定義の振る舞いのままにしておく要因は何ですか?
最初の主要な要因は次のとおりです。特定のプログラムの動作に同意しない、市場での言語の既存の実装が2つありますか?...
次の主な要因は次のとおりです。この機能は、実装のさまざまな可能性を自然に提示しますか。...
3番目の要因は次のとおりです。機能は非常に複雑なため、正確な動作を詳細に分類するのは難しいか、指定するのに費用がかかりますか?...
4番目の要因は、この機能がコンパイラーに高い負荷を課して分析するかどうかです。...
5番目の要因は次のとおりです。この機能はランタイム環境に高い負荷をかけますか?...
第六の要因は次のとおりです。行動に定義排除してにいくつかの主要な最適化を行っていますか?...
これらは頭に浮かぶいくつかの要因にすぎません。もちろん、言語設計委員会が機能を「実装定義」または「未定義」にする前に議論する他の多くの要因があります。
上記は非常に短い取材です。記事全体には、この抜粋で言及したポイントの説明と例が含まれています。それは非常に価値の読み。たとえば、「6番目の要因」に与えられた詳細は、Javaメモリモデル(JSR 133)の多くのステートメントの動機に関する洞察を与え、いくつかの最適化が許可される理由を理解するのに役立ちます。前発生や因果関係の要件などの制限。
記事の資料はどれも私にとって特に新しいものではありませんが、このようにエレガントで簡潔で理解可能な方法で提示されたものを見たことがあるなら、私は気の毒に思うでしょう。すごい。
少なくとも、C ++と同じ意味では、Javaには未定義の動作はないと思います。
この理由は、C ++の背後にあるものとJavaの背後にある異なる哲学があるためです。Javaの中心的な設計目標は、プラットフォームを超えてプログラムを変更せずに実行できるようにすることでした。したがって、仕様ではすべてを非常に明示的に定義しています。
対照的に、CおよびC ++の中心的な設計目標は効率です。必要がない場合でも、パフォーマンスを犠牲にする機能(プラットフォームに依存しないことを含む)はありません。そのため、仕様では一部のプラットフォームで余分な作業が発生し、特定のプラットフォーム専用のプログラムを作成し、そのすべての特異性を認識している人でもパフォーマンスが低下するため、仕様では意図的に一部の動作を定義しません。
まさにその理由で、Javaが限定的な未定義の動作を遡及的に導入することを強制された例さえあります:strictfpキーワードはJava 1.2で導入され、浮動小数点計算が仕様が以前に要求していたIEEE 754標準に従うことから逸脱することを可能にしますなぜなら、そうすると余分な作業が必要になり、一部の一般的なCPUですべての浮動小数点計算が遅くなりますが、実際には場合によってはより悪い結果を生むからです。
int x=-1; foo(); x<<=1;
ハイパーモダンの哲学を考えると、書き直しが優先されるfoo
ため、終了しないパスは到達不能でなければなりません。これfoo
はif (should_launch_missiles) { launch_missiles(); exit(1); }
、コンパイラがif である場合、それを単純化することができます(そして、一部の人々はそうすべきです)launch_missiles(); exit(1);
。従来のUBはランダムなコード実行でしたが、これは時間と因果律の法則に縛られていました。改良された新しいUBはどちらにも拘束されません。
Javaは、正確には以前の言語の教訓のために、未定義の振る舞いを根絶するためにかなり努力します。たとえば、クラスレベルの変数は自動的に初期化されます。ローカル変数はパフォーマンス上の理由から自動初期化されませんが、これを検出できるプログラムを誰も作成できないようにする洗練されたデータフロー分析があります。参照はポインターではないため、無効な参照は存在できず、逆参照null
は特定の例外を引き起こします。
もちろん、完全に指定されていない動作がいくつか残っており、それらがそうであると仮定すれば、信頼できないプログラムを書くことができます。たとえば、通常の(ソートされていない)を反復処理する場合Set
、言語は各要素を一度だけ表示することを保証しますが、表示される順序は保証しません。連続した実行で順序は同じかもしれませんし、変わるかもしれません。または、他の割り当てが発生しない限り、またはJDKなどを更新しない限り、同じままである可能性があります。そのような効果をすべて取り除くことはほぼ不可能です。たとえば、すべてのコレクション操作を明示的に順序付けまたはランダム化する必要がありますが、これは小さな追加の未定義ネスの価値はありません。
「未定義の動作」とその起源を理解する必要があります。
未定義の動作とは、標準で定義されていない動作を意味します。C / C ++には、さまざまなコンパイラ実装と追加機能が多すぎます。これらの追加機能は、コードをコンパイラに結び付けました。これは、集中的な言語開発がなかったためです。そのため、一部のコンパイラの高度な機能の一部は「未定義の動作」になりました。
一方、Javaでは言語仕様はSun-Oracleによって制御されており、仕様を作成しようとしている人は他にいないため、未定義の動作はありません。
質問に具体的に答えて編集
Javaは、C / C ++に見られる未定義の動作を本質的にすべて排除します。(例:符号付き整数オーバーフロー、ゼロによる除算、初期化されていない変数、nullポインターの逆参照、ビット幅以上のシフト、ダブルフリー、「ソースコードの終わりに改行なし」。)しかし、Javaには、プログラマーはめったに遭遇しません。
Java Native Interface(JNI)、JavaがCまたはC ++コードを呼び出す方法。関数の署名を間違えたり、JVMサービスへの無効な呼び出しを行ったり、メモリを破損したり、不正なものを割り当てたり解放したりするなど、JNIを台無しにする多くの方法があります。以前にこれらの間違いを犯しましたが、一般に、JNIコードを実行する1つのスレッドがエラーをコミットすると、JVM全体がクラッシュします。
Thread.stop()
、これは非推奨です。見積もり:
なぜ
Thread.stop
非推奨ですか?本質的に安全ではないからです。スレッドを停止すると、スレッドがロックしたすべてのモニターのロックが解除されます。(
ThreadDeath
これらのモニターによって以前に保護されたオブジェクトのいずれかが一貫性のない状態にあった場合、他のスレッドはこれらのオブジェクトを一貫性のない状態で表示する可能性があります。このようなオブジェクトは破損していると言われています。スレッドが破損したオブジェクトで動作すると、任意の動作が発生する可能性があります。この動作は微妙で検出が難しい場合があります。他の未チェックの例外とは異なり、ThreadDeath
スレッドをサイレントに強制終了します。したがって、ユーザーは自分のプログラムが破損している可能性があるという警告はありません。破損は、実際の損傷が発生した後、数時間または数日後でもいつでも現れます。
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html