Fortran F2003多态代码内存泄漏问题求助
Fortran F2003多态类型赋值内存泄漏原因分析
问题场景
在物理领域代码中使用F2003多态特性时,出现内存占用持续增长直至工作站冻结的问题,以下是可复现的精简代码:
module functions_defs implicit none type, abstract :: funcroot character(len=:),allocatable :: funcname contains procedure(func), deferred :: eval end type funcroot interface function func(this, x) result(fx) import :: funcroot class(funcroot), intent(inout) :: this real*8, intent(in) :: x real*8 :: fx end function func end interface end module functions_defs module functions use functions_defs implicit none type, extends(funcroot) :: funcroot1 real*8 :: a,b contains procedure :: eval => func1 end type contains function func1(this, x) result(fx) class(funcroot1), intent(inout) :: this real*8, intent(in) :: x real*8 :: fx fx=this%a*x*x-this%b end function func1 end module functions program testleak use functions implicit none class(funcroot),allocatable :: myfunc myfunc = funcroot1(a=1.0d0,b=1.0d0,funcname="function1") end program testleak
测试结果
- 使用gfortran 7.5.0测试时,valgrind检测到内存泄漏;将赋值语句放入无限循环:
do myfunc = funcroot1(a=1.0d0,b=1.0d0,funcname="function1") end dotop命令显示内存持续增长。 - 后续测试Ubuntu 22.04的gfortran 11.3、MinGW gfortran 13.1版本,均存在相同问题。
- 使用Intel Fortran 2021.9.0时,无限循环版本无内存增长,但valgrind仍提示可能存在字节丢失。
修复后的代码
修改代码(避免显式声明分配多态变量、替换real*8为标准类型参数)后,在gfortran 7.5与valgrind下运行正常:
module utils use, intrinsic :: iso_fortran_env implicit none integer, parameter :: rp = REAL64 end module utils module functions_defs use utils implicit none type, abstract :: funcroot character(len=:), allocatable :: funcname contains procedure(func), deferred :: eval end type funcroot interface function func(this, x) result(fx) import :: funcroot,rp class(funcroot), intent(inout) :: this real(kind=rp), intent(in) :: x real(kind=rp) :: fx end function func end interface end module functions_defs module functions use functions_defs implicit none type, extends(funcroot) :: funcroot1 real(kind=rp) :: a,b contains procedure :: eval => func1 end type contains function func1(this, x) result(fx) class(funcroot1), intent(inout) :: this real(kind=rp), intent(in) :: x real(kind=rp) :: fx fx=this%a*x*x-this%b end function func1 end module functions module print use functions implicit none contains subroutine printf(f) class(funcroot) :: f write(*,*) "Function: "//trim(f%funcname) end subroutine printf end module print program testleak use print implicit none do call printf(funcroot1(a=1.0_rp,b=1.0_rp,funcname="function1")) end do end program testleak
泄漏原因分析
gfortran编译器实现缺陷:原代码中
class(funcroot),allocatable :: myfunc是可分配多态变量,每次执行myfunc = funcroot1(...)时,编译器需先释放myfunc当前指向的对象内存,再分配新对象。但gfortran 7.x至13.x版本在处理包含可分配字符组件的抽象父类型赋值时,未正确触发父类型可分配组件的析构逻辑,导致funcname的内存无法被释放,形成累积泄漏。修复代码的核心逻辑:修改后的代码直接将临时
funcroot1对象传递给子程序,临时对象的生命周期由编译器自动管理,子程序调用结束后会正确释放其可分配组件内存,因此无泄漏。Intel Fortran的特殊表现:Intel编译器对多态赋值的析构逻辑处理更完善,能正确释放父类型可分配组件,因此无限循环下无内存增长;valgrind提示的"可能字节丢失"是编译器内部管理的临时内存未被识别,并非实际泄漏。
注:替换real*8为REAL64属于代码规范优化,并非解决泄漏的直接原因,核心修复点是避免了多态可分配变量的重复赋值操作。
内容的提问来源于stack exchange,提问作者naffrancois
相关产品推荐
相关产品推荐

