You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 do
    
    top命令显示内存持续增长。
  • 后续测试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

泄漏原因分析

  1. gfortran编译器实现缺陷:原代码中class(funcroot),allocatable :: myfunc是可分配多态变量,每次执行myfunc = funcroot1(...)时,编译器需先释放myfunc当前指向的对象内存,再分配新对象。但gfortran 7.x至13.x版本在处理包含可分配字符组件的抽象父类型赋值时,未正确触发父类型可分配组件的析构逻辑,导致funcname的内存无法被释放,形成累积泄漏。

  2. 修复代码的核心逻辑:修改后的代码直接将临时funcroot1对象传递给子程序,临时对象的生命周期由编译器自动管理,子程序调用结束后会正确释放其可分配组件内存,因此无泄漏。

  3. Intel Fortran的特殊表现:Intel编译器对多态赋值的析构逻辑处理更完善,能正确释放父类型可分配组件,因此无限循环下无内存增长;valgrind提示的"可能字节丢失"是编译器内部管理的临时内存未被识别,并非实际泄漏。

注:替换real*8为REAL64属于代码规范优化,并非解决泄漏的直接原因,核心修复点是避免了多态可分配变量的重复赋值操作。

内容的提问来源于stack exchange,提问作者naffrancois

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 02:40:32