オブジェクト指向Fortranを使ってコードを書いていたら、使用メモリ量がどんどん増えていくバグに遭遇しました。deallocateしているはずなのにどんどん増えていきます。
問題のコード
コードはこんな感じです。
program main
use test
implicit none
integer::N
complex(8),allocatable::vec_data(:)
N = 100000
allocate(vec_data(N))
vec_data = pmap(N,wrap)
contains
function wrap(i)
integer,intent(in)::i
complex(8)::wrap
type(type_test),allocatable::testdata
allocate(testdata)
testdata = type_test(N)
wrap = 0d0
deallocate(testdata)
end function
end program
ここで、type_test
というクラスはmodule testで定義されており、
module test
implicit none
type type_test
class(type_inner),allocatable::vec
end type
interface type_test
module procedure::init_test
end interface
type type_inner
complex(8),allocatable::x(:)
end type
interface type_inner
module procedure::init_inner
end interface
contains
type(type_test) function init_test(N) result(testdata)
integer::N
allocate(testdata%vec)
testdata%vec = init_inner(N)
end function
type(type_inner) function init_inner(N) result(vec)
integer::N,i
allocate(vec%x(1:N))
do i=1,N
vec%x(i) = i
end do
end function
function pmap(N,func) result(vec_data)
interface
function func(i)
integer,intent(in)::i
complex(8)::func
end function
end interface
integer,intent(in)::N
integer::i
complex(8)::v
complex(8)::vec_data(N)
do i=1,N
vec_data(i) = func(i)
end do
end function
end module
です。フィールドにtype_inner
というクラスを持ち、そのクラスの中で配列が確保されています。ですので、構造体のフィールドに構造体がありそのフィールドに配列、というものを用意しています。
結果
moduleを上に、mainを下に書いてgfortranでコンパイルして実行すると、なぜか使用メモリが増えていきます。deallocate(testdata)
しているのに、メモリが増えていきます。
- Mac OS 10.14および10.15
- gfortran 9と10
で確認しました。
原因は不明です。
追記
@cure_honeyさんのコメントの通り、
type(type_test) function init_test(N) result(testdata)
integer::N
type(type_inner) :: dummy
dummy = init_inner(n)
testdata%vec = dummy
end function
とすることでメモリーリークが消えることが確認できました。
@implicit_none さんの提案のfinalizer処理の追加
type type_test
class(type_inner),allocatable::vec
contains
final :: vec_final ! finalizer
end type
subroutine vec_final(this)
use, intrinsic :: iso_fortran_env
implicit none
type(type_test) :: this ! not "class"
if (allocated(this%vec)) deallocate (this%vec)
end subroutine vec_final
をやってみましたが、問題は解決できませんでした。deallocateを明示的に書いてもやってくれないのは、@cure_honeyさんのおっしゃるclassの具体形がわからないからでしょうか...