简化查询以提高内存使用率:OutOfMemory异常

时间:2016-11-10 21:01:32

标签: .net f#

我有一个控制台应用程序连接到远程dB并运行多个查询。

我正在使用64位F#来构建应用程序。我对此充满信心

C:\Program Files (x86)\Microsoft SDKs\F#\4.0\Framework\v4.0\fsc.exe 

在构建过程中用作F#框架路径。此外,上述文件路径位于我的PATH环境变量

每个查询的结构都是

query{expression}
|> Seq.toArray
|> Array.map (fun q -> {a = q.a; 
                        b = q.b; 
                        etc...}
|> writeToJson ("filePath")

我写入JSON文件,因为这些查询结果在应用程序的下游使用。

我在查询中使用Array,因为我已经包含了

<runtime>
 <gcAllowVeryLargeObjects enabled="true" />
</runtime>

在我的App.config文件中。 This page建议包含此

  

...启用总大小超过2千兆字节(GB)的数组。

不幸的是,在我的第二次查询中,我得到OutOfMemory例外。

查询

    query {
        for header in db.CustomerDetails do
        leftOuterJoin row in db.MDR_0916
            on (header.PID = row.PID) into result
        for row in result do
        select ([row.APSci; 
                    row.Charter; 
                    row.VirtualSchool; 
                    row.AffIndicator], header)
}
|> Seq.toArray
|> Array.map (fun (row, header) -> {CustId = Option.ofNullable header.CBSCUSTOMERID; 
                                  IDType = Option.ofObj header.IDTYPE; 
                                  Name = Option.ofObj header.CUSTOMERNAME;
                                  State = Option.ofObj header.STATE;
                                  AcctStatus = Option.ofObj header.ACCOUNTSTATUS;
                                  Contract = Option.ofObj header.CONTRACT;
                                  ContractDesc = Option.ofObj header.CONTRACTDESCRIPTION;
                                  AcctMgr = Option.ofObj header.ACCOUNTMANAGER;
                                  InstType = Option.ofObj header.CUSTOMERNAME;
                                  InstDesc = Option.ofObj header.INSTITUTIONTYPEDECRIPTION; 
                                  MDR = Some {HasApSci = (row |> List.item 0); 
                                              IsCharter = (row |> List.item 1); 
                                              IsVirtualSchool = (row |> List.item 2); 
                                              AffRating = (row |> List.item 3)}})
|> writeToJson (Path.Combine(recEngTresorFolder, "rawCustomers.json"))

返回〜250万条结果。我正在查询查询结果的sizeof<RawCustomer>8

有谁知道如何防止OOM异常发生?在gcAllowVeryLargeObjects和使用F#64位之间,我原以为会照顾它。

1 个答案:

答案 0 :(得分:1)

基本上,您的应用程序必须进行调试,如果不访问数据库和您的环境,将会很困难。

仅仅因为你正在调用fsc,这并不意味着它是64位(尽管通常是这样)。编译器的各种标志在.fsproj文件中设置,通常是通过Visual Studio,但您可以手动编辑它。

enter image description here

例如,我可以创建一个32位的配置文件:

<Prefer32Bit>true</Prefer32Bit>  要么: <PlatformTarget>x64</PlatformTarget> <DocumentationFile>bin\Release\ConsoleApplication8.XML</DocumentationFile> <Prefer32Bit>false</Prefer32Bit>

您可以在FSIAnyCpu.exe中开始测试,这是64位版本的FSI(应该是VSCode的默认设置,可以在VS2015中设置)。

  1. 然后你应该确保你可以直接在SQL中实际运行服务器上的查询。

  2. 下一步,我假设您正在使用某种类型提供程序来访问数据库,请确保清除连接。例如,为每个查询使用单独的datacontext,并将其与use绑定,不要让它以这种方式处理它,GC将清理。对于某些数据库,您可以禁用对象跟踪,并且由于您正在从DB读取,因此无论如何这都无关紧要。

  3. 正如评论中提到的那样,尝试使用Seq

  4. 来延迟显示结果
  5. 最后,当您转换为JSON时,尝试使用某种StreamWriter,因此它不会阻塞。

  6. 但是最后你必须通过仔细测试(读取添加一些睡眠和打印语句......)并在任务管理器上观察内存来隔离导致内存不足异常的代码片段当然最好使用Performance Profiler是VS2015或Redgate的.NET Profiler之类的东西。

    作为初学者,您需要在查询(或其中一个查询)或Json导出器中找到异常的原因。

    添加1:

    这个小片段会给你8GB阵列没问题。所以你可以用它来测试。阵列长度仍然有限​​,每个维度大约20亿个元素。

    open System
    
    [<EntryPoint>]
    let main argv =
    
        let x = Int32.MaxValue /2 
        printfn "%A" x 
        let big = Array.init x (fun _ -> "aa") 
        Console.ReadLine() |> ignore
        printfn "%A" big.Length
    
        0