You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何这段Haskell代码内存占用持续增长直至崩溃?

Why Your Haskell Code Is Leaking Memory

Great question—let’s unpack exactly why your crash function is eating up memory until it crashes, even though you’re using foldl' (the strict fold you’d expect to avoid leaks).

The Core Issue: Weak Head Normal Form (WHNF) vs. Full Evaluation

You’re right that foldl' replaces the accumulator each step and evaluates it to Weak Head Normal Form (WHNF). But here’s the catch: WHNF only evaluates the outermost constructor of a value, not its inner components.

Your accumulator is a tuple (Int, Int). When foldl' evaluates this tuple to WHNF, it just confirms "this is a tuple"—it doesn’t force evaluation of the two Int values inside the tuple. Those Ints can remain as unevaluated thunks (pending computations), and that’s where your leak happens.

Let’s Trace the Thunk Build-Up

Look at how next is computed in your spin function:

next | indexNIncrement >= spinNumber = 1 + (indexNIncrement `rem` spinNumber)
     | otherwise = 1 + indexNIncrement
  • In the first branch, rem is a strict operation—it forces indexNIncrement (and thus index + increment) to be fully evaluated. No thunk builds up here.
  • In the second branch, next becomes 1 + index + increment—this creates a new thunk. Since foldl' only evaluates the tuple constructor (not the Int inside), this thunk becomes the new index in your accumulator.

With 50 million iterations, if most steps hit the second branch, you end up with a massive chain of nested thunks like 1 + (1 + (1 + (... + increment)...)). Each thunk references the previous one, so none can be garbage collected until the entire fold completes. By then, you’ve already eaten up all available memory.

Fixing the Leak: Force Full Evaluation of Accumulator Components

To stop the thunk build-up, you need to ensure the Int values in your accumulator are fully evaluated at each step. Here are two straightforward ways:

1. Use Bang Patterns (Simplest Approach)

Enable the BangPatterns extension to mark the accumulator’s components as strict. This forces Haskell to evaluate them before proceeding:

{-# LANGUAGE BangPatterns #-}

crash :: Int -> Int -> Int
crash increment spins = snd $ foldl' spin' (0,0) [1..spins]
  where
    spin' = spin increment
    spin increment !(!index, !element1) spinNumber = (next, nextElementOne)
      where
        next | indexNIncrement >= spinNumber = 1 + (indexNIncrement `rem` spinNumber)
             | otherwise = 1 + indexNIncrement
        indexNIncrement = index + increment
        nextElementOne | next == 1 = spinNumber
                       | otherwise = element1

The ! symbols tell GHC to fully evaluate index and element1 before running the rest of spin.

2. Use seq for Manual Strictness

If you prefer not to use extensions, you can use seq to force evaluation of the new accumulator values before returning them:

crash :: Int -> Int -> Int
crash increment spins = snd $ foldl' spin' (0,0) [1..spins]
  where
    spin' = spin increment
    spin increment (index, element1) spinNumber = 
        next `seq` nextElementOne `seq` (next, nextElementOne)
      where
        next | indexNIncrement >= spinNumber = 1 + (indexNIncrement `rem` spinNumber)
             | otherwise = 1 + indexNIncrement
        indexNIncrement = index + increment
        nextElementOne | next == 1 = spinNumber
                       | otherwise = element1

seq a b evaluates a to WHNF before returning b. Here, we ensure both next and nextElementOne are fully evaluated before packing them into the tuple.

Why Your Initial Assumption Was Off

You wondered why the old accumulator values aren’t freed—because the new accumulator’s thunk references the old one. Until the thunk is evaluated, the garbage collector can’t clean up those previous values. With strict evaluation of the accumulator components, each old value is no longer referenced by the new thunk, so it gets collected immediately.

内容的提问来源于stack exchange,提问作者Quinn Wilson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:40:47