今回はC# Job Systemを使ってるとよく見るエラーについてです。
- A Native Collection has not been disposed, resulting in a memory leak.
- JobTempAlloc has allocations that are more than 4 frames old - this is not allowed and likely a leak
- nvalidOperationException: The previously scheduled job AnyComponent:AnyJobName writes to the NativeArray AnyJobName.anyfield.
- InvalidOperationException: The native container has been declared as [ReadOnly] in the job, but you are writing to it.
- InvalidOperationException: The native container has been declared as [WriteOnly] in the job, but you are reading from it.
- InvalidOperationException: AnyJob.anyField is not a value type. Job structs may not contain any reference types.
- FindObjectFromInstanceID can only be called from the main thread.
- IndexOutOfRangeException: Index 0 is out of restricted IJobParallelFor range
- ArgumentException: System.Boolean used in NativeArray<System.Boolean> must be blittable
- 関連
A Native Collection has not been disposed, resulting in a memory leak.
Googleで日本語にすると以下のような訳になります。
ネイティブコレクションが破棄されていないため、メモリリークが発生します。
このエラーは内容の通りで、NativeArray等を生成した後に後片付けしてない場合に発生します。
Awake/OnDestroyやOnEnable/OnDisableのように対応した環境でちゃんと破棄しないとコレが出るので注意が必要です。
破棄はDestroyではなくDisposeです。
JobTempAlloc has allocations that are more than 4 frames old - this is not allowed and likely a leak
Googleで日本語にすると、以下のような訳になります。
JobTempAllocは4フレーム以上古い割り当てを持っています - これは許可されておらず、漏れそうです
これはAllocatorがTempやTempJobに設定したNativeArrayが、複数のフレームを跨いだ時に発生します。
返却期限を超えないようにNativeArrayを破棄しておけばOKです。
nvalidOperationException: The previously scheduled job AnyComponent:AnyJobName writes to the NativeArray AnyJobName.anyfield.
Googleで日本語にすると、以下のような訳になります。
nvalidOperationException:以前にスケジュールされたジョブAnyComponent:AnyJobNameがNativeArray AnyJobName.anyフィールドに書き込みます。
これは少しややこしくて、複数のJobが同一のNativeArrayにアクセスしていた場合に発生します。
普通はNativeArrayに同時に書き込んだり、順不同で書き込んだ時になにかデータがおかしくなる的なバグなのですが、C# Job Systemでは複数のジョブから同一のコンテナにへ書き込む可能性がある実装に対してエラーを出すようになっています。要素単位ではない点に注意。*1
この「書き込むか、書き込まないのか」については、[ReadOnly][WriteOnly]属性で判断しています。また、どちらの属性も無い場合には「書き込むし読み込む」という状態になります。
つまり、この問題を解決するならば、明確に同時にアクセスしないように定義する、もしくはジョブが同時に実行されないようにするが回答となります。
例えばReadOnlyを設定して明示的に同時アクセスしないようにする、
https://gist.github.com/tsubaki/862adcd1d5922ac2dfddd25bed8b0f97#file-anycomponent-cs-L46
またはスケジュールを発行する際に依存関係を設定して、同時に実行しないようにする…といったモノが考えられます。
https://gist.github.com/tsubaki/776470e2067ff6e542d884853c4e55f3#file-anycomponent-cs-L32
なお、Jobを使い回す(参照先のNativeArrayだけ書き換えてScheduleを発行)のような場合にもこのエラーが出ます。
InvalidOperationException: The native container has been declared as [ReadOnly] in the job, but you are writing to it.
[ReadOnly]を設定したコンテナにアクセスしたオブジェクトに書込が発生した場合にエラーとして出力されます。
注意点として「書込が発生したら」エラーになるらしく、ビルド時には分かりません。
InvalidOperationException: The native container has been declared as [WriteOnly] in the job, but you are reading from it.
[ReadOnly]を設定したコンテナにアクセスしたオブジェクトに書込が発生した場合にエラーとして出力されます。
こちらも「読込が発生したらエラー」になります。
例えば writeOnlyData += n; みたいなコードを書くと発生します。
InvalidOperationException: AnyJob.anyField is not a value type. Job structs may not contain any reference types.
JobSystem内で参照型のオブジェクトにアクセスした場合に発生します。例えば JobのフィールドにComponentを登録して、アクセスしようとしても出来ません。
対策は「参照型クラスを使うな」です。C# Jobsystemでは基本的にNativeArrayに結果を格納して、それを料理してもらう形になります。
FindObjectFromInstanceID can only be called from the main thread.
Unityのコンポーネントやインスタンスにメインスレッド以外からアクセスしようとすると発生します。
IndexOutOfRangeException: Index 0 is out of restricted IJobParallelFor range
IJobParallelForを使用した場合に、indexで指定された要素以外の要素にアクセスが発生した発生します。実際に書き込まれるのか等は関係なくエラーになります。
対策としては、明示的にReadOnlyを付けて書込が発生しないようにすればOKです。
https://gist.github.com/tsubaki/af86f8891780dd4c692e04b7b1352165
ArgumentException: System.Boolean used in NativeArray<System.Boolean> must be blittable
NativeArrayに使用できるのはblittable型のみです。boolは非blittable型なので使用できません。
なので、int型にして 0 or 1 みたいな懐かしい感じのことをする必要があります。
Blittable 型と非 Blittable 型 | Microsoft Docs
関連
*1:要素単位の場合はJobParallelForを使う