2012-01-11 25 views
12

私は "CLR via C#"を読んでいましたが、この例では、最初に 'obj'に割り当てられたオブジェクトは、行1が実行された後ではなく、Javaのオブジェクト寿命

ローカル変数の有効期間は定義されているスコープではなく、前回の読み込み時に定義されているからです。

私の質問は、Javaはどうですか?私はそのような動作をチェックするためにこのプログラムを書いており、オブジェクトが生きているように見えます。私は、JVMがバイトコードを解釈している間に変数の生存期間を制限することはできないと考えているので、メソッドコンパイルを強制するために 'java -Xcomp'でプログラムを実行しようとしましたが、 'finalize'は呼び出されません。 Javaの場合はそうではないようですが、ここでより正確な答えを得ることができれば幸いです。また、AndroidのDalvik VMはどうですか?プロジェクトの目標は、 'リリース' である場合(タイマーをMSの.Netに一度だけ呼ばれる

public static void Main (string[] args) 
{ 
    var timer = new Timer(TimerCallback, null, 0, 1000); // call every second 
    Console.ReadLine(); 
} 

public static void TimerCallback(Object o) 
{ 
    Console.WriteLine("Callback!"); 
    GC.Collect(); 
} 

TimerCallback:

class TestProgram { 

    public static void main(String[] args) { 
     TestProgram ref = new TestProgram(); 
     System.gc(); 
    } 

    @Override 
    protected void finalize() { 
     System.out.println("finalized"); 
    } 
} 

追加されました: ジェフリー・リヒターは、 "C#を経由でCLR" にこのような何かをするコード例を示しますGC.Collect()呼び出し後に破棄され、ターゲットが 'デバッグ'の場合は1秒ごとに呼び出されます(プログラマーはデバッガでオブジェクトにアクセスできます)。しかし、あなたがそれをどのようにコンパイルしても、毎秒呼び出されるMonoコールバックで。 MonoのTimer実装は、スレッドプールのどこかのインスタンスへの参照を格納するように見えます。 MSの実装はこれをしません。

+10

これは以前のJava仕様では見つかりませんでした。 .NETはあなたが予想していたよりもさらに狂っている可能性があることに注意してください。*インスタンスのメソッドが実行されている間*インスタンスが収集される可能性があります。 –

答えて

4

オブジェクトが収集されているという理由だけで、実際に任意の時点で収集されるというわけではありません。そのため、方法では偽陰性が生じる可能性があります。オブジェクトのfinalizeメソッドが呼び出された場合は、それが到達不能であったと言うことはできますが、メソッドが呼び出されていなければ、論理的に何かを推論することはできません。ほとんどのGC関連の質問と同様に、ガベージコレクタの非決定論は、それが何をするのか正確にテスト/保証することを難しくしています。到達可能性/回収のトピックに関する

、JLSは言う(12.6.1):

到達オブジェクトは、任意のライブスレッドから任意の潜在的な継続的計算でアクセス可能な任意のオブジェクトです。プログラムの変換を最適化することで、到達可能なオブジェクトの数を、到達不能であると考えられるオブジェクトの数よりも少なくすることができます。例えば、コンパイラまたはコードジェネレータは、もはやヌルに使用されない変数またはパラメータを設定して、そのようなオブジェクトの記憶域を潜在的に再要求可能にすることを選択することができる。

私は上記の段落は "あなたが間違いなくそれ以上使用しない場合は到達不可能"と同形であると思います。

元の状況に戻ると、は、2行目とは対照的に、1行目以降に到達不能と見なされるオブジェクト間の実用的な影響を考えることができますか??私の最初の反応は何もないということです。もしあなたが何らかの形でこのような状況を見つけることができれば、言語の固有の弱点ではなく、VMが苦労するような悪い/ねじれたコードの兆候になるでしょう。

私は反論にはなっていますが。


編集:面白い例をありがとう。

私はあなたの評価に同意し、どこに行くのかを見ていますが、デバッグモードがコードのセマンティクスを微妙に変更している可能性があります。

書かれたコードでは、Timerを、そのスコープ内で後で読み取られないローカル変数に割り当てます。最も些細なエスケープ解析でも、の方法ではtimerの変数が他の場所で使用されていないことが分かり、これを省略することができます。したがって、私はあなたの最初の行は、単に直接コンストラクタを呼び出すとまったく同じと考えることができると思います。この後者の場合には

public static void Main (string[] args) 
{ 
    new Timer(TimerCallback, null, 0, 1000); // call every second 
    ... 

それは(」それはdoesnのことを仮定して、新しく作成されたTimerオブジェクトが作成直後到達できないことは明らかですそのコンストラクタ内の静的フィールドなどに自身を追加するなど、何もしないでください)。それがGCに回ってすぐに収集されるようにします。

デバッグの場合、開発者が後でメソッドのローカル変数の状態を検査したいと思っているという理由で、状況が微妙に異なります。したがって、コンパイラ(およびJITコンパイラ)はこれらを最適化することはできません。メソッドの終わりに変数のアクセスがあるかのように、その時点までのコレクションを防止します。

それでも、私は実際にセマンティクスを変更するとは思わない。 GCの性質は、コレクションがほとんど保証されないということです(少なくともJavaでは、OutOfMemoryErrorがスローされた場合、到達不能とみなされるものはすべて直前にGCedされたという唯一の保証が得られます)。実際、ランタイムの存続期間中に作成されたすべてのオブジェクトを保持するのに十分なヒープスペースがあると仮定すると、ノーオペレーションGC実装は完全に有効です。だから、あなたは何度も行動の変化を観察するかもしれないが、それはあなたがそれを呼び出す方法に基づいて何を見るかについての保証はないので、これはうまくいく。 (これは、概念的には、CPU負荷の高いタスク全体で実行されるタイマーが、システムが負荷をかけられているときに何度もチェックする方法と似ています)。

At私はこの答えの最初の文に戻ります。 :)

+0

お返事ありがとうございます。私は自分の質問を編集し、そこにコード例を追加します。 – Ivan

+1

自分の投稿にコード例を追加しました。その最適化によって引き起こされる異なるビルドターゲットを使用したネット。 – Ivan

+0

環境の下での可変寿命!=言語内の変数寿命は、このような動作は直感的ではなく、理論上はコードを破ることができますが、それは本当に問題だとは思いません。 Javaの仕様がそのようなケースについて何かを言っているのかどうか疑問に思うだけです。 GCを使用すると、毎日もっと複雑に見えます:) – Ivan

1

Javaは一般的にオブジェクトがスコープ内で到達可能な場合(ガベージではない参照がある場合)、オブジェクトはガーベジではないという動作をします。これは再帰的なものなので、が、bへの参照を持つオブジェクトへの参照である場合、オブジェクトbは、ガベージではないことを示します。それでもrefが参照するオブジェクトに到達できる範囲での

は、 refはゴミではない、(あなたが行 System.out.println(ref.toString())を追加することもできます)。

しかし、this old source from Sun's siteによると、ほとんどの場合、JVMの特定の実装に依存します。