释放使用newCString分配的内存

Vla*_*eev 10 memory haskell memory-management ffi

正如库文档所说CString创建的newCString必须通过free函数释放.我一直在期待CString创建它时需要一些内存,当它被释放时free内存使用会下降,但它没有!这是示例代码:

module Main where

import Foreign
import Foreign.C.String
import System.IO

wait = do
  putStr "Press enter" >> hFlush stdout
  _ <- getLine
  return ()

main = do
  let s = concat $ replicate 1000000 ['0'..'9']
  cs <- newCString s
  cs `seq` wait   -- (1)

  free cs
  wait   -- (2)
Run Code Online (Sandbox Code Playgroud)

当程序停在(1)时,htop程序显示内存使用量大约为410M - 这没关系.我按回车键,程序在第(2)行停止,但内存使用率仍为410M,尽管cs已经是freed!

这怎么可能?用C编写的类似程序就像它应该的那样.我在这里错过了什么?

Don*_*art 8

问题是free只向垃圾收集器指示它现在可以收集字符串.这实际上并没有强制垃圾收集器运行 - 它只是表明CString现在是垃圾.根据堆压力启发式,仍然由GC决定何时运行.

您可以通过直接呼叫强制主要收集,这会立即将内存减少到5M左右.performGCfree

例如这个程序:

import Foreign
import Foreign.C.String
import System.IO
import System.Mem

wait = do
  putStr "Press enter" >> hFlush stdout
  _ <- getLine
  return ()

main = do
  let s = concat $ replicate 1000000 ['0'..'9']
  cs <- newCString s
  cs `seq` wait   -- (1)

  free cs
  performGC
  wait   -- (2)
Run Code Online (Sandbox Code Playgroud)

行为符合预期,具有以下内存配置文件 - 第一个红点是调用performGC,立即解除分配字符串.然后程序在大约5M左右徘徊直到终止.

在此输入图像描述

  • 我想我无法与你的证据证明`performGC'确实有效,但这是我认为的答案,直到我去查看[来源](http://hackage.haskell.org/packages/archive /base/4.5.0.0/doc/html/src/Foreign-Marshal-Alloc.html#free) - 在我看来,像`free`确实调用C函数`free()`.它如何与GC交互? (2认同)
  • 我怀疑是因为在GC运行之前,内存实际上并未从GHC rts池释放到操作系统.所以你可以重用Haskell的内存块,它还没有回到操作系统. (2认同)
  • 但是肯定malloc和free根本不使用rts池,它们直接使用系统内存? (2认同)