uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

JNAで死ぬほどはまった件


MacOS XおよびLinuxで、JNA(Java Native Access)を通じて、putenv("A=B")すると、即座もしくは数秒後〜数十秒後に設定したはずの環境変数が消え去るという現象に遭遇した。

消え去るまでの時間が不定なので、何かを修正して、「直った」と思ったら実は発現までの時間が長いだけで、待ってたら発現するというのを何度も繰り返し。

最後まで突き詰めたわけではないのだが今は以下の仮説に落ち着いている。

  • putenvは、引数に渡した文字列を静的変数かなんかにコピーでなしに参照で保持し*1、それが環境領域として使われる(getenvでとれたり、サブプロセスに渡されたり、という意味で)。
  • JNAは、引数が文字列(char*)の場合、Stringから何らかのコピーを行った上、そのコピーをC関数(DLLで定義されたルーチン)に渡すようだ。そのコピーはGCの対象となるようだ。
  • GC時に、putenvした環境変数が消える。

おいおいおーい。

対処として、引数のStringを参照を保持しても無意味。なぜならGCされるのはコピーだから。JNIだったらpinningするとこだがJNAなので探した限りでは回避策みあたらず。

え?どうしたかって?putenvをやめてsetenvにしましたよ。
でもsetenvはWIndows環境にはないから(それがもともとputenvを使ってた理由だけど)、OSに応じて切り替えるようにした。

strtok等でも生じるかは不明。

*1:参照で保持するのは、MacOS Xだけでなく[http://www.linux.or.jp/JM/html/LDP_man-pages/man3/putenv.3.html:title=libc4 と libc5 と glibc 2.1.2以降でも同様の動作であり、SUSv2(Single UNIX Specification Version 2)仕様上これが正しいらしい]。