使用指针的 Fortran 链表中的内存泄漏

Memory leak in Fortran linked list using pointers

我正在尝试在 Fortran 中创建一个链表结构,用于计算区域中粒子之间的定点迭代。粒子在计算区域中被迭代追踪,它们每一步的属性都被存储;并且它们与上一次迭代的粒子属性相互作用。

对于这个问题,我有两个链表,一个包含上一次迭代的粒子属性(list_use,当前通过域跟踪的粒子与之交互),另一个列表累积属性粒子在计算区域中的轨迹。在一次迭代之后(即在所有粒子都被跟踪过一次域之后),我想丢弃 list_use(已经计算了与此数据的交互),将 list_buildup 复制到 list_use 和然后丢弃 list_buildup,以便可以用迭代中的下一个数据重新填充它。

我似乎在复制和丢弃列表时发生了内存泄漏。这是复制内存泄漏的一小段代码。据我所知,泄漏发生在 updateASR。我希望这个子例程之前的进程内存等于它之后的内存,但是使用 VisualStudio 上的诊断,它显示每次调用 updateASR 时内存都会增加,最终导致程序终止(带有访问冲突错误)。 Here's an image showing the VS process memory diagnostic. 我猜想 destroyASREntries 是不是在做我真正想要它做的事情?

我对 Fortran 中的指针不是很有经验,因此有点卡住了,所以非常感谢任何帮助!

module linked_list

!---------------------------------------------------------------------------------
! Type containing the data for an ASR entry, used to compute interactions between rays.
type ASR_entry
    real                :: intensity    !<- The intensity of the ASR entry
    real                :: ang_freq     !<- Angular frequency
    real,dimension(3)   :: wavevector   !<- Wavevector (x,y,z): Cartesian.
end type ASR_entry

!---------------------------------------------------------------------------------
! A node type in the linked list for the ASR.
type ASR_Node
    type(ASR_Node),pointer  :: next => null()
    type(ASR_Node),pointer  :: prev => null()
    type(ASR_entry)         :: node_entry
end type ASR_Node

!---------------------------------------------------------------------------------
! For interaction, each cell contains one of these ASR linked lists, which itself contains the nodes, which contain the entry.
type ASR_cell_ll
    type(ASR_Node),pointer  :: head =>  null()  !<- first%next points to first node
    type(ASR_Node),pointer  :: last =>  null()  !<- last%prev points to last node
    integer(kind=4)         :: size = 0         !<- Number of ASR entries in the linked list
end type ASR_cell_ll

contains
!---------------------------------------------------------------------------------
! Create the ASR linked list in every cell.
subroutine createASRcell(list)
    implicit none
    type(ASR_cell_ll), pointer :: list
    
    if(associated(list)) call Abort("Must pass null pointer of type 'ASR_cell_ll' to createASRcell.")
    
    !- Allocate memory - is this necessary??
    allocate(list)
    allocate(list%head,list%last)
    list%head%next  => list%last    !<- If list is empty, then the first entry points to the last entry which is null
    list%last%prev  => list%head
    list%size       = 0
    
end subroutine createASRcell

!---------------------------------------------------------------------------------
! Delete all ASR entries
subroutine destroyASREntries(list)
    implicit none
    type(ASR_cell_ll), pointer  :: list
    type(ASR_Node), pointer     :: dCurrent=>null(), dNext=>null()
    
    if (.not. associated(list)) return
    allocate(dCurrent,dNext)
    
    dCurrent    => list%head
    dNext       => dCurrent%next
    
    !- Deallocate all data nodes in list
    do
        nullify(dCurrent%prev)  !- Remove dangling pointers from the list structure.
        deallocate(dCurrent)
        if (.not. associated(dNext)) exit
        dCurrent    => dNext
        dNext       => dCurrent%next
    end do
    
    nullify(dCurrent,dNext) !- Remove dangling pointers
    
    list%size=0
    deallocate(list)
    
end subroutine destroyASREntries

!---------------------------------------------------------------------------------
!- This subroutine removes the old entries in list_use, copies the list_buildup entries into list_use, then empties list_buildip for the next iteration.
subroutine updateASR(list_use, list_buildup)
    implicit none
    type(ASR_cell_ll),pointer   :: list_use, list_buildup
    
    !First destroy all entries from the previous ASR iteration, before recreating the list.
    call destroyASREntries(list_use)
    call createASRcell(list_use)
    
    !Then make the use list the previous iterations buildup list.
    list_use => list_buildup
    
    !The stop buildup from pointing to the use list's new entries, before recreating buildup as blank.
    nullify(list_buildup)
    call createASRcell(list_buildup)
    
end subroutine updateASR

end module linked_list

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

module definitions
    implicit none
    integer :: nx,ny,nz,nbeams  !Dimensions of the linked list domain.
    integer :: ix,iy,iz,ibeam   !Loop variables
    
end module definitions

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

program main
    use definitions
    use linked_list
    implicit none
    type(asr_cell_ll),pointer   :: list_use,list_buildup    !<-The temporary and used linked list.
    integer :: i
    
    call createASRcell(list_buildup)
    call createASRcell(list_use)
    
    do i=1,1000000000
        call updateASR(list_use,list_buildup)
    enddo
    
end program main

以上是我用ifort编译的

首先,让我们看一下createASRcell。它 returns 一个 ASR_cell_llsize=0。那你为什么要分配内存呢?当你想要一个节点时,你应该只分配一个节点。我觉得createASRcell应该是

subroutine createASRcell(list)
  type(ASR_cell_ll), pointer :: list
      
  if(associated(list)) call Abort("Must pass null pointer of type 'ASR_cell_ll' to createASRcell.")

  list%head => null()
  list%last => null()
  list%size = 0      
end subroutine

其次,我们来看destroyASREntries。行数

allocate(dCurrent,dNext)
  
dCurrent => list%head
dNext => dCurrent%next

正在 dCurrentdNext 创建两个节点,然后立即失去对这些节点的跟踪以将 dCurrentdNext 指向新目标。这将泄漏您刚刚分配的内存。 allocate 语句不应该存在。还有很多多余的重新分配正在进行。简化子程序,我们得到

subroutine destroyASREntries(list)
  type(ASR_cell_ll), pointer :: list

  type(ASR_Node), pointer :: dCurrent, dNext
  
  if (.not. associated(list)) return

  dCurrent => list%head
  
  !- Deallocate all data nodes in list
  do while(associated(dCurrent))
    dNext => dCurrent%next
    nullify(dCurrent%prev)
    nullify(dCurrent%next)
    deallocate(dCurrent)
    dCurrent => dNext
  end do

  ! - Deallocate the list itself
  deallocate(list)
end subroutine destroyASREntries

最后来看updateASR。我不太明白你想在这里做什么,但子程序会导致问题。行数

call destroyASREntries(list_use)
call createASRcell(list_use)
list_use => list_buildup

将清理 list_use 指向的旧 ASR_cell_ll,创建一个新的空 ASR_cell_ll,再次由 list_use 指向,然后立即失去对通过将 list_use 指向 list_buildup 来创建这个新列表。这将泄漏新创建的 ASR_cell_ll.

的所有内存

感谢@veryreverie 的回答,帮助解决了漏洞并消除了我的误解。问题是由于在 createASRcelldestroyASREntries 中分配指针然后将它们重新指向新内存。 Here is the diagnotic with the new code showing no memory leak.如果有人感兴趣,这是没有内存泄漏的修改后的工作代码:

module linked_list

!---------------------------------------------------------------------------------
! Type containing the data for an ASR entry, used to compute interactions between rays.
type ASR_entry
    real                :: intensity    !<- The intensity of the ASR entry
    real                :: ang_freq     !<- Angular frequency
    real,dimension(3)   :: wavevector   !<- Wavevector (x,y,z): Cartesian.
end type ASR_entry

!---------------------------------------------------------------------------------
! A node type in the linked list for the ASR.
type ASR_Node
    type(ASR_Node),pointer  :: next => null()
    type(ASR_Node),pointer  :: prev => null()
    type(ASR_entry)         :: node_entry
end type ASR_Node

!---------------------------------------------------------------------------------
! For interaction, each cell contains one of these ASR linked lists, which itself contains the nodes, which contain the entry.
type ASR_cell_ll
    type(ASR_Node),pointer  :: head =>  null()  !<- first%next points to first node
    type(ASR_Node),pointer  :: last =>  null()  !<- last%prev points to last node
    integer(kind=4)         :: size = 0         !<- Number of ASR entries in the linked list
end type ASR_cell_ll

contains
!---------------------------------------------------------------------------------
! Create the ASR linked list in every cell.
subroutine createASRcell(list)
    implicit none
    type(ASR_cell_ll), pointer :: list
    
    if(associated(list)) call Abort("Must pass null pointer of type 'ASR_cell_ll' to createASRcell.")
    
    allocate(list)
    allocate(list%head,list%last)
    
    list%head%next  => list%last    !<- If list is empty, then the first entry points to the last entry which is null
    list%last%prev  => list%head
    list%size       = 0
    
end subroutine createASRcell

!---------------------------------------------------------------------------------
! Delete all ASR entries
subroutine destroyASREntries(list)
    implicit none
    type(ASR_cell_ll), pointer  :: list
    type(ASR_Node), pointer     :: dCurrent=>null(), dNext=>null()
    
    if (.not. associated(list)) return
    
    dCurrent    => list%head
    
    !- Deallocate all data nodes in list
    do while(associated(dCurrent))
        dNext => dCurrent%next
        nullify(dCurrent%prev)  !- Remove dangling pointers from the list structure.
        nullify(dCurrent%next)  !- Remove dangling pointers from the list structure.
        deallocate(dCurrent)
        dCurrent    => dNext
    end do
    
    ! - Deallocate the list itself
    deallocate(list)
    
end subroutine destroyASREntries

!---------------------------------------------------------------------------------
!- This subroutine removes the old entries in list_use, copies the list_buildup entries into list_use, then empties list_buildip for the next iteration.
subroutine updateASR(list_use, list_buildup)
    implicit none
    type(ASR_cell_ll),pointer   :: list_use, list_buildup
    
    call destroyASREntries(list_use)    !First destroy all entries from the previous ASR iteration  
    list_use => list_buildup            !Then make the use list the previous iterations buildup list.
    nullify(list_buildup)               !The stop buildup from pointing to the use list's new entries
    
end subroutine updateASR

end module linked_list

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

module definitions
    implicit none
    integer :: nx,ny,nz,nbeams  !Dimensions of the linked list domain.
    integer :: ix,iy,iz,ibeam   !Loop variables
    
end module definitions

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

program main
    use definitions
    use linked_list
    implicit none
    type(asr_cell_ll),pointer   :: list_use=>null(),list_buildup=>null()    !<-The temporary and used linked list.
    integer :: i
    
    call createASRcell(list_buildup)
    call createASRcell(list_use)
    
    do i=1,1000000000
        call updateASR(list_use,list_buildup)
    enddo
    
end program main