ByteArrayOutputStream を使い回すコードを書きそうでいて、
めったに書かないコーディングである。
reset() を呼ばずに使い回すと前回の出力内容が残っていて思わぬ結果になる。
reset() は、ByteArrayOutputStream 内部で持つ byte配列の書き込みカウンタを0にしてるだけなのである。
public synchronized void reset() { count = 0; }
実際に格納される byte配列 を 0個の配列にするわけでもなくメモリ消費は変わらない。
toString() の実装が、
public synchronized String toString() { return new String(buf, 0, count); }
なので、reset() の count = 0 で、前回、buf に蓄積された内容の影響を
受けることはない。
close() に至っては、ByteArrayOutputStream では何もしない。
flush() は、OutputStream を継承していても Override して何かをするわけでもない。
となると、reset() を使うようにして使い回すということに注意すれば
良いことになる。
try-with-resources 対象として書いても ByteArrayOutputStream は何の効果もない。
byte配列も GCが走るまでは、メモリを使いっぱなしということになる。
ヒステリックに書くなら、、、
ByteArrayOutputStream bo = new ByteArrayOutputStream();
この bo に対して目的を果たした後で、、
Field bufField = ByteArrayOutputStream.class.getDeclaredField("buf"); bufField.setAccessible(true); bufField.set(bo, new byte[]{});
と、new byte[]{} をセットすれば、メモリの使用も抑えられる。
ただし、Java9 以降では
jdk.internal.module.IllegalAccessLogger の出力を抑制しないと。。
try{ Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); sun.misc.Unsafe u = (sun.misc.Unsafe) theUnsafe.get(null); Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger"); Field logger = cls.getDeclaredField("logger"); u.putObjectVolatile(cls, u.staticFieldOffset(logger), null); }catch(Exception e){ }
標準エラー警告でログが出てしまう。
WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by Xxxxxxxxxxxxxxxxxxxxxx to field java.io.ByteArrayOutputStream.buf WARNING: Please consider reporting this to the maintainers of Xxxxxxxxxxxxx WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release