開発者の皆さん、こんにちは!
SO はほとんどの場合、私のプログラミングの問題を解決するのに役立ちました。これは本当に私も同僚も理解できない奇妙な現象です。動作するサンプルを提供できなくて申し訳ありませんが、プロジェクトは分解するには複雑であり、適切に実行するには特定のハードウェアが必要です。なので頑張って説明していきます。
私たちのプロジェクトの基盤は、Java アプリケーション ( JNA )を介してプロジェクト固有のハードウェアにアクセスするためのネイティブ ライブラリ (この場合は 32 ビット Windows C-DLL ) です。目的は、Swing UI で (USB 経由で接続された) ハードウェアの独自のファイルシステムを管理および表示することです。多くのネイティブ ライブラリとドライバーを Java アプリケーションに統合したため、これは私たちにとって非常に一般的なプロジェクト構成です。
概要:デバイスを列挙するための単体テストは正常に機能します。ネイティブ ライブラリのモジュールは、メモリを割り当て、接続されたデバイスの情報を含む構造体で埋めます。それは良い習慣ではありませんが、私たちはこの部分に何の影響も与えていないので、それに従う必要があります. この構造体を Java/JNA にマップし、ネイティブ関数を呼び出し、構造体の内容を Java 転送クラスにコピーして、コンソールに出力しました。うまく動作します。
デバイスの列挙中にアクティブな UI 操作がある場合、ネイティブ ライブラリがアクセス違反でクラッシュするようになりました。この UI 操作がライブラリとは関係なくても。JNA エラー メッセージには EXCEPTION_ACCESS_VIOLATION (0xc0000005) が表示されますが、これは SO の調査で無効/空のメモリであることが明らかになりました。
誰もそのような問題に遭遇したことがありますか? 私たちは確かに決してしませんでした。エラーの原因をコードのこの部分に絞り込むのに何日もかかりました。ネイティブ ライブラリが関係している場合、デバッグは容易ではありません。JVM メモリの同時実行性に問題がある可能性はありますか? ネイティブ ライブラリはそれ自体でメモリを割り当て、JVM はそれについて何も知らないので、JVM は新しい Swing コンポーネントに既に使用されているメモリにメモリを割り当てようとしますか?
コード: 次のスニペットは、私の単体テストからのもので、可能な限り分解されています。意図した順序は明らかです。ルートノードからノードを削除し、接続されたデバイスをロードして、これらのデバイスを新しいノードとして追加します。このコードはアクセス違反でクラッシュしますが、ネイティブ コールではなく、ツリー コンポーネントにアクセスするとすぐにクラッシュします。
public void loadDevices(){
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
rootNode.removeAllChildren();
rootNode.add(new LoadingNode());
tree.expandPath(new TreePath(rootNode));
}
});
final List<Device> devices = lib.loadDevices(); // wrapped native call
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
rootNode.removeAllChildren();
if(!devices.isEmpty()){
for (Device dev : devices ) {
DevNode node = new DevNode(dev);
rootNode.add(node);
}
}
}
});
}
注: DevNodeにはネイティブ データは含まれず、各ネイティブ構造体の内容は Java 転送オブジェクトにコピーされます。アンマネージ コードはすべてlib#loadDevices()メソッドでローカルに処理されるため、オブジェクト データを移動しようとするときに GC で問題が発生することはありません。
ノードを作成する代わりに、 SwingUtilitiesへの呼び出しを完全に削除し、結果のデバイス情報をコンソールに出力すると、この部分は正常に機能します。JTreeまたはTreeModelメンバーにアクセスしようとするとすぐに、コードがクラッシュします。これをSwingUtitilies#invokeLater()の呼び出しまたは同じスレッドで実行しても問題ありません。
これは非常に具体的な問題であり、誰も関心を持たないでしょう (SO/Google で解決策を検索するのが非常に困難な理由)。しかし、おそらく私は幸運で、誰かがすでにこの問題に遭遇しています。
お久しぶり
ザンダー
編集:もともと、このコードはワーカー スレッドにラップされていたため、同じ結果になりました。これは私の単体テストのほんの一部です。
編集 2:説明が不十分だったか、ここで重要なことを言い忘れていたようです。申し訳ありません。ツリーまたはそのモデルへのアクセスは、必ずしもネイティブ ライブラリと関係があるとは限りません。コードをもう一度見てください。invokeLater の最初の呼び出しは、ツリーからノードを削除するだけです。invokeLater の 2 番目の呼び出しを削除しても、ネイティブ ライブラリがクラッシュします。