Reputation: 101
I've wrote my first program using allocatable. It works as expected. But, does it really? And more importantly, how can I set up a unit-test to catch memory leaks?
The idea behind the program is to allocate a chunck of storage room for my list of objects in the first place. And every time I add one more element more to the list than the allocated size, I double the allocation. I do this to reduce the number of allocations and subsequent copying of data from the old allocated memory, to the newly allocated memory.
I might over complicate this, but I'd like to spend some time now understanding the pitfalls, rather than falling head first into them a year or two down into the project.
The ode is compiled with gfortran 8.3.0 on linux. And using pFUnit 4.1. The code below is an extract to test only the allocation part.
Heres my test-program:
program test_realloc
use class_test_malloc
integer :: i
real :: x, y
type(tmalloc) :: myobject
call myobject%initialize()
do i=1, 100
x = i * i
y = sqrt(x)
call myobject%add_nbcell(i, x, y)
end do
call myobject%dump()
end program test_realloc
array_reallocation.f:
!
! Simple test to see if my understanding of dynamicly allocation
! of arrays is correct.
!
module class_test_malloc
use testinglistobj
implicit none
type tmalloc
integer :: numnbcells, maxnbcells
type(listobj), allocatable :: nbcells(:)
contains
procedure, public :: initialize => init
procedure, public :: add_nbcell ! Might be private?
procedure, private :: expand_nbcells
procedure, public :: dump
end type tmalloc
contains
subroutine init(this)
class(tmalloc), intent(inout) :: this
this%numnbcells = 0
this%maxnbcells = 4
allocate (this%nbcells(this%maxnbcells))
end subroutine init
subroutine add_nbcell(this, idx, x, y)
class(tmalloc), intent(inout) :: this
integer, intent(in) :: idx
real, intent(in) :: x, y
type(listobj) :: nbcell
if(this%numnbcells .eq. this%maxnbcells) then
call this%expand_nbcells()
print *,"Expanding"
endif
this%numnbcells = this%numnbcells + 1
nbcell%idx = idx
nbcell%x = x
nbcell%y = y
this%nbcells(this%numnbcells) = nbcell
print *,"Adding"
end subroutine add_nbcell
subroutine expand_nbcells(this)
class(tmalloc), intent(inout) :: this
type(listobj), allocatable :: tmpnbcells(:)
integer :: size
size = this%maxnbcells *2
allocate (tmpnbcells(size))
tmpnbcells(1:this%maxnbcells) = this%nbcells
call move_alloc( from=tmpnbcells, to=this%nbcells)
this%maxnbcells = size
end subroutine
subroutine dump(this)
class(tmalloc), intent(inout) :: this
integer :: i
do i=1, this%numnbcells
print*, this%nbcells(i)%x, this%nbcells(i)%y
end do
end subroutine
end module
listobj.f:
module testinglistobj
type listobj
integer :: idx
real :: x
real :: y
end type
end module testinglistobj
Upvotes: 0
Views: 425
Reputation: 7434
You will not get any memory leaks with this code. The reason is, and this is fundamental to the understanding of allocatable arrays, is that in Fortran 95 onwards it is required that allocatable
arrays without the save
attribute automatically get deallocated when they go out of scope. The nett result of this is that memory leaks for such arrays are impossible. This is one very good reason why you should prefer allocatable
arrays to pointer
s. Related is the general software engineering principle of keeping the scope of variables as limited as possible, so that arrays are in memory for as short a period as possible.
Note this does not mean that you should never deallocate them as an array may remain in scope long after it is actually useful. Here "manual" deallocation may be of use. But it is not a memory leak.
Upvotes: 1