内存管理 – Fortran内存分配不会产生错误,但程序在初始化时被操作系统杀死

鉴于下面提供的最小工作示例,您知道为什么在内存分配步骤中不会发生内存分配错误吗?正如我检查的那样,当我使用valgrind来运行代码,或者将参数source = 0.0添加到内存分配语句时,我就像预期的那样,有内存分配错误.

更新:我用最小的工作示例重现了这个问题:

program memory_test

  implicit none

  double precision, dimension(:,:,:,:), allocatable :: sensitivity
  double precision, allocatable :: sa(:)
  double precision, allocatable :: sa2(:)

  integer :: ierr,nnz
  integer :: nx,ny,nz,ndata

  nx = 50
  ny = 50
  nz = 100
  ndata = 1600

  allocate(sensitivity(nx,ny,nz,ndata),stat=ierr)

  sensitivity = 1.0

  nnz = 100000000

  !allocate(sa(nnz),source=dble(0.0),stat=ierr)
  allocate(sa(nnz),stat=ierr)
  if(ierr /= 0) print*, 'Memory error!'

  !allocate(sa2(nnz),source=dble(0.0),stat=ierr)
  allocate(sa2(nnz),stat=ierr)
  if(ierr /= 0) print*, 'Memory error!'

  print*, 'Start initialization'

  sa = 0.0
  sa2 = 0.0

  print*, 'End initialization'

end program memory_test

当我运行它时,我没有消息“内存错误!”打印,但有消息’开始初始化’然后该程序被操作系统杀死.如果我使用’source’参数进行内存分配(如代码中所述),那么我就会收到消息’Memory error!’.

对于内存统计,’free’命令给出了这个输出:

total       used       free     shared    buffers     cached
Mem:       8169952    3630284    4539668      46240       1684     124888
-/+ buffers/cache:    3503712    4666240
Swap:            0          0          0
您正在看到Linux使用的内存分配策略的行为.当您分配内存但尚未写入内存时,它仅包含在虚拟内存中(请注意,这也可能受特定Fortran运行时库的影响,但我不确定).此内存存在于进程虚拟地址空间中,但不受任何实际物理内存页面的支持.只有当您写入内存时,才会分配物理页面,并且只能满足写入要求.

考虑以下程序:

program test
   implicit none
   real,allocatable :: array(:) 

   allocate(array(1000000000)) !4 gb array

   print *,'Check memory - 4 GB allocated'
   read *

   array(1:1000000) = 1.0

   print *,'Check memory - 4 MB assigned'
   read *

   array(1000000:100000000) = 2.0

   print *,'Check memory - 400 MB assigned'
   read *

   array = 5.0

   print *,'Check memory - 4 GB assigned'
   read *

end program

该程序分配4 GB内存然后写入4 MB阵列部分,396 MB阵列部分(总写入数= 400 MB),最后写入完整阵列(总写入数= 4 GB).程序在每次写入之间暂停,以便您可以查看内存使用情况.

在分配之后,在第一次写入之前:

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                           
29192 casey     20   0 3921188   1176   1052 S   0.0  0.0   0:00.00 fortranalloc

所有内存都是虚拟的(VIRT),只有一小部分由物理内存(RES)支持.

4 MB写完后:

29192 casey     20   0 3921188   5992   1984 S   0.0  0.0   0:00.00 fortranalloc

写完396 MB之后:

29192 casey     20   0 3921188 392752   1984 S   0.0  1.6   0:00.18 fortranalloc

并在4 GB写入后:

29192 casey     20   0 3921188 3.727g   1984 S  56.6 15.8   0:01.88 fortranalloc

注意,在每次写入之后,驻留存储器增加以满足写入.这表明实际的物理内存分配仅在写入时发生,而不仅仅在分配时发生,因此正常的allocate()无法检测错误.当您将source参数添加到allocate时,会发生写入,这会导致内存的完全物理分配,如果失败,则可以检测到错误.

您可能会看到的是当内存耗尽时调用的linux OOM Killer.当发生这种情况时,OOM Killer将使用算法来确定要释放内存的内容,并且代码的行为使其很可能被杀死.当您的写入导致可以满足的物理分配时,您的进程将被内核杀死.您在写入时看到它(由赋值引起)但由于上面详述的行为而没有分配.

相关文章
相关标签/搜索