Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

for the memory leakage

cepheid
Beginner
1,164 Views
The test code is as bellow:

[bash]  1 module DataMOD
  2   type data
  3      real :: x
  4      type(data),pointer  :: next
  5   end type data
  6   
  7   interface operator(+)
  8      module procedure DataAdd
  9   end interface operator(+)
 10   
 11 contains
 12
 13   recursive function DataAdd( d1, d2) result(d)
 14     implicit none
 15     type(data),intent(in) :: d1, d2
 16     type(data)            :: d
 17     type(data),pointer  :: pd1, pd2, pd
 18     
 19     d%x = d1%x + d2%x
 20     if(associated(d1%next))then
 21        allocate(d%next)
 22        pd1 => d1%next
 23        pd2 => d2%next
 24        pd => d%next
 25        pd = pd1 + pd2
 26     endif
 27   end function DataAdd
 28   
 29 end module DataMOD
30
 31 program test
 32   use DataMOD
 33   implicit none
 34   
 35   type(data) :: d1, d2, d
 36   
 37   allocate(d1%next, d2%next)
 38   d1%next%x = 3.
 39   d2%next%x = 4.
 40
 41   d = d1 + d2
 42 end program test[/bash]





After compiling, and running with the valgrind, there's the memory leakage

==2160== 16 bytes in 1 blocks are definitely lost in loss record 3 of 3
==2160== at 0x4C2993D: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2160== by 0x4006D3: __datamod_MOD_dataadd (test.f90:21)
==2160== by 0x4007D1: MAIN__ (test.f90:43)
==2160== by 0x400820: main (test.f90:32)

How to avoid this leakage?
0 Kudos
8 Replies
jimdempseyatthecove
Honored Contributor III
1,164 Views
Objects to which pointers point are not auto-deallocated when the pointer "expires" (exit function/subroutine containing local pointer, or exiting function/subroutine containing local object containing pointer(your case), ordeallocating object containing pointer). Basically the same functionality that happens in C/C++.

Look in the IVF documentationfor FINAL. You can use FINAL subroutine(s) to perform cleanup operations upon object distruction (deallocate or exiting scope of life of object (including temporaryobject as result from expression)).

*** caution ***

Ensure that no more than one data%next points to the same object.
Also, (not affecting memory leak) IF block on line 20 assumes if d1%next is associated that d2%next is also associated. This may or may not be true. You should handle that condition or issue an assert fail.

Jim Dempsey
0 Kudos
cepheid
Beginner
1,164 Views
What I mean is that the operator function "DataAdd" has the memory
leakage, and how to deallocate this?
0 Kudos
Steven_L_Intel1
Employee
1,164 Views
First of all, valgrind will often report memory leaks that are not really there in Fortran programs because it does not understand Fortran memory management. Second, there is an error in your code in that you do not give pointer component next an initial NULL() value - this makes the ASSOCIATED test on it invalid. One fix would be to change the component declaration to:

type(data),pointer :: next => null()

When I make this change and run it with Intel Inspector XE, I find no memory leaks in the program.
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,164 Views
Steve,

The user has a memory leak due to programming error.

d, declared at line 16 (but represents in this case d declared at line 35), gets a data object allocated to its next at line 21. There is no corrisponding deallocate to that d's next pointer.

Had the user looked at my message regarding FINAL, they would find outthat he can create a FINAL subroutine for his data object (type) that can deallocate the object pointed to by its next pointer.

Note, he should have the => NULL in the constructor as you suggest.

Jim Dempsey
0 Kudos
cepheid
Beginner
1,164 Views
I'v modified the code as bellow.

[bash]module DataMOD
  type data
     real :: x
     type(data),pointer  :: next=>null()
   contains
     FINAL :: RemoveData
  end type data

  interface operator(+)
     module procedure DataAdd
  end interface operator(+)

contains

  recursive function DataAdd( d1, d2) result(d)
    implicit none
    type(data),intent(in) :: d1, d2
    type(data)            :: d
    type(data),pointer  :: pd1, pd2, pd

    d%x = d1%x + d2%x
    if(associated(d1%next))then
       allocate(d%next)
       pd1 => d1%next
       pd2 => d2%next
       pd => d%next
       pd = pd1 + pd2
    endif
  end function DataAdd

  recursive function trans(d) result(od)
    implicit none
    type(data),intent(in) :: d
    type(data)            :: od
    od%x = d%x + 5.
    if(associated(d%next))then
       allocate(od%next)
       od%next = trans(d%next)
    endif
  end function trans

  recursive subroutine RemoveData(D)
    implicit none
    type(data) :: D
    if(associated(D%next)) call RemoveData(D%next)
    deallocate(D%next)
    write(*,*)"RemoveData"
  end subroutine RemoveData

  subroutine ddd
    ! use DataMOD
    implicit none

    type(data) :: d1, d2, d, dd

    d1%x = 1.
    d2%x = 2.
    allocate(d1%next, d2%next)
    d1%next%x = 3.
    d2%next%x = 4.

    d = d1 + d2

    dd = trans(d1)
  end subroutine ddd

end module DataMOD


program test
  use DataMOD
  call ddd
end program test

[/bash]
There's also some error: *** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0000000001da20a0 ***.


please give me some clue.

If i don't want to use "FINAL Subroutine", how should I modify the code by manual deallocate?
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,164 Views

"double free" the same object being returned (deallocated)twice. I suggest you add trace code to aid you in following what is happening. Look for object copy operations (=) where a pointer might get duplicated.

Also, your DataAdd and trans functionsassume the first level next pointers are null. While this may be true in your subroutine ddd. It may not be true for general use in your application. You may need to directly call your FINAL routine to properly cleanup the prior allocations prior to making new allocations.

DataAdd:

if(associated(d%next)) RemoveData(d)
allocate(d%next)

trans:

if(associated(od%next)) RemoveData(od)
allocate(od%next)

Don't omit tracing. Add it as conditional code.

Jim Dempsey

0 Kudos
cepheid
Beginner
1,164 Views
Sir,
Again as your direct, the code is modified as bellow:

[bash]module DataMOD
  type data
     real :: x
     type(data),pointer  :: next=>null()
   ! contains
   !   FINAL :: RemoveData
  end type data

  interface operator(+)
     module procedure DataAdd
  end interface operator(+)

  interface assignment(=)
     module procedure DataAssign
  end interface assignment(=)

contains

  recursive function DataAdd( d1, d2) result(d)
    implicit none
    type(data),intent(in) :: d1, d2
    type(data)            :: d
    type(data),pointer  :: pd1, pd2, pd

    d%x = d1%x + d2%x
    if(associated(d1%next))then
       call RemoveData(d)
       allocate(d%next)
       pd1 => d1%next
       pd2 => d2%next
       pd => d%next
       pd = pd1 + pd2
    endif
  end function DataAdd

  recursive subroutine DataAssign(od, id)
    implicit none
    type(data),intent(in)  :: id
    type(data),intent(out) :: od
    od%x = id%x
    if(associated(id%next))then
       call RemoveData(od)
       allocate(od%next)
       call DataAssign(od%next, id%next)
    endif
  end subroutine DataAssign

  recursive function trans(d) result(od)
    implicit none
    type(data),intent(in) :: d
    type(data)            :: od
    od%x = d%x + 5.
    if(associated(d%next))then
       call RemoveData(od)
       allocate(od%next)
       od%next = trans(d%next)
    endif
  end function trans

  recursive subroutine RemoveData(D)
    implicit none
    type(data) :: D
    if(associated(D%next)) then
       call RemoveData(D%next)
       deallocate(D%next)
    endif
    write(*,*)"RemoveData"
  end subroutine RemoveData

  subroutine ddd
    ! use DataMOD
    implicit none

    type(data) :: d1, d2, d, dd

    d1%x = 1.
    d2%x = 2.
    allocate(d1%next, d2%next)
    d1%next%x = 3.
    d2%next%x = 4.

    d = d1 + d2

    dd = trans(d1)
    call RemoveData(d1)
    call RemoveData(d2)
    call RemoveData(d)
    call RemoveData(dd)
  end subroutine ddd

end module DataMOD


program test
  use DataMOD
  
  call ddd

end program test
[/bash]

After run in valgrind, the memory leakage errors are:

==6470== 16 bytes in 1 blocks are definitely lost in loss record 1 of 2
==6470== at 0x4C2993D: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6470== by 0x400D42: __datamod_MOD_dataadd (test.f90:28)
==6470== by 0x400999: __datamod_MOD_ddd (test.f90:82)
==6470== by 0x400E10: MAIN__ (test.f90:97)
==6470== by 0x400E46: main (test.f90:95)
==6470==
==6470== 16 bytes in 1 blocks are definitely lost in loss record 2 of 2
==6470== at 0x4C2993D: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6470== by 0x400B9F: __datamod_MOD_trans (test.f90:55)
==6470== by 0x4009E6: __datamod_MOD_ddd (test.f90:84)
==6470== by 0x400E10: MAIN__ (test.f90:97)
==6470== by 0x400E46: main (test.f90:95)
==6470==
==6470== LEAK SUMMARY:
==6470== definitely lost: 32 bytes in 2 blocks
==6470== indirectly lost: 0 bytes in 0 blocks
==6470== possibly lost: 0 bytes in 0 blocks
==6470== still reachable: 0 bytes in 0 blocks
==6470== suppressed: 0 bytes in 0 blocks

how to avoid these memory leakage without using the FINAL subroutine? how to modified the above code?
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,164 Views
RemoveData must NULLIFY D%next after deallocate of D%next

As your code is currently written, the top most levelremains non-NULL at point of return from subroutine ddd, and thus when FINAL is run on d1, d2, d, dd those nodes's %nextwill be deallocated a second time (as well as the now junk data in what appears to be in those next pointer, etc....

The calls to RemoveData on lines 85:88 should not be required since when you return from ddd, the FINAL subroutine will be automatically called for d1, d2, d, dd.

As per earlier post, add trace code to observe what is going on

allocate(od%next)
write(*,*) 'ddd allocate', loc(od), loc(od%next)
...
write(*,*) 'RemoveData deallocate', loc(D), loc(D%next)
deallocate(D%next)


insert similar code in all places of allocation and deallocations, annotate write with function/subroutine name as above. Use FORMAT statement to display loc in Hex such that you can corrilate addresses with valgrind (or use calculator for conversion).

By observing your trace output and comparing with valgrind you should discover an inconsistancy. Knowing the inconsistancy (object addresswho's next is being allocated to or deallocated from, you can then step through your program and when at that point in your code you can observe current state of current call stack level and eachhigher call stack level. At some point you will find the problem.

Jim Dempsey
0 Kudos
Reply