【问题标题】:VB.net Memory efficient function neededVB.net 需要内存高效功能
【发布时间】:2010-08-31 15:52:59
【问题描述】:

当 RowCollection 为 50000+ 时,以下函数会出现内存不足的异常,因此我需要提高内存效率。该函数只需要构造一个逗号分隔的字符串,存储在 RowCollection 中的行索引。任何人都可以在下面发现任何明显的内存消耗操作吗?

N.B RowCollection 仅包含存储为整数的行索引列表。

 Private Function GetCommaSeparatedString(ByRef RowIndexes As ArrayList) As String
        Dim RowString As String = String.Empty

        'Build a string of the row indexes 
        'Add one onto each index value so our indexes begin at 1 
        For Each Row In RowIndexes 
            RowString += CInt(Row.ToString) + 1 & ","
        Next

        'Remove the last comma
        If RowString.Length > 0 Then
            RowString = RowString.Substring(0, RowString.Length - 1)
        End If

        Return RowString
    End Function

提前致谢。

【问题讨论】:

  • 正确的拼写是“分隔”。
  • 顺便说一句,为什么要ArrayList?这看起来像是 List<int> 的工作。

标签: .net performance arraylist memory-management string-concatenation


【解决方案1】:

我不确定你为什么会出现内存不足错误,除非你的行的字符串表示非常大,因为你永远不会有超过一两个不可回收的字符串。

但是,您的方法 非常低效,因为它花费大量时间复制半构建字符串的内容。 StringBuilder 在构建大字符串时更合适,因为它可以被修改而无需每次都重新创建内容。

但是,在这种情况下,即使是 StringBuilder 也是一个坏主意,因为您正在连接字符串并且已经有一种方法可以做到这一点:String.Join。只需使用 LINQ 查询执行 add-one-to-index-stuff 即可获得单行:

Private Function GetCommaSeparatedString(ByVal RowIndexes As ArrayList) As String
    Return String.Join(",", From index In RowIndexes Select CInt(index) + 1)
End Function

我还建议不要通过引用传递,除非您确实需要它。您没有修改 RowIndexes,因此按值传递它。我也不确定你为什么要 ToString()-ing 索引然后立即解析它。他们不是已经整数了吗?只需使用 CInt。

【讨论】:

  • 我完全同意你的看法,尽管如果可能的话,我可能会用这种方法重新设计代码。
  • @PhilipRieck 您所说的“流式传输”是什么意思?
  • @Strilanc 要么创建一个 IEnumerable 方法来获取行,要么通过附加逗号等的文本阅读器读取它 - 也就是说,不要尝试一次从内存中处理整个文件。
  • @Strilanc 感谢所有 cmets 到目前为止...我无法流式传输结果,因此使用字符串生成器或 String.Join 似乎是最好的方法。上面的行在 Option Strict On 下会变成什么?我还没有开始使用 Linq。谢谢
  • 问题已解决。感谢您的帮助。
【解决方案2】:

更新:虽然这是使用 stringbuilder 的直接更改,但请查看 StrilancSteven Sudit 的更好方法

好吧,您可能仍然会耗尽内存(毕竟,内存有限的),但您应该使用 StringBuilder,而不是连接字符串。每次,您都在创建一个新的字符串对象而不是更改它(因为字符串是不可变的)

Private Function GetCommaSeparatedString(ByRef RowIndexes As ArrayList) As String
    Dim RowString As New StringBuilder()

    'Build a string of the row indexes 
    'Add one onto each index value so our indexes begin at 1 
    For Each Row In RowIndexes 
        RowString.AppendFormat("{0},",  CInt(Row.ToString) + 1)
    Next

    'Remove the last comma
    If RowString.Length > 0 Then
        RowString.Append(RowString.Substring(0, RowString.Length - 1))
    End If

    Return RowString
End Function

【讨论】:

  • @Steven Sudit 有我想要的答案 - 流式传输,或者根据需要创建并丢弃每一行。我无法立即想到一个需要将全部内容作为字符串的有效设计。
【解决方案3】:

StringBuilder 是个好主意,但为什么不直接通过流式输出而不是尝试将其全部保存在内存中来避免问题呢?

【讨论】:

    【解决方案4】:

    这是因为在每次迭代中,您在幕后创建了 2 个字符串,并且它们在接近尾声时变得越来越大。

    "1,2,3,4,5,....499,500" "1,2,3,4,5,....499,500,"

    在仅 500 次迭代结束时,您正在创建 2 个近 2000 个字符长的字符串,只是在下一次迭代中将它们丢弃(但运行时可能会保留它们)。

    在最后一次迭代中,假设您的行索引是连续的,您的字符串(从 1 到 50000)将是 100,000 个字符长。这意味着您已经分配了大约 10,000,000,000 个字符或(我相信 2 个字节/字符)20 GB 的字符串。

    您可以首先在字符串 (RowString) 上使用 StringBuilder 而不是 +=
    Ex

    Dim RowString As StringBuilder = new StringBuilder( 100000 )
    
    For Each Row In RowIndexes 
        RowString.Append( CInt(Row.ToString) + 1).Append( "," )
    Next
    
    '...'
    
    Return RowString.ToString
    

    您也可以尝试下一个,但您应该对两者进行分析并选择最适合您的。

    Private Function GetCommaSeperatedString(ByRef RowIndexes As ArrayList) As String
        Dim indexArray as String[] = RowIndexes
                                     .Select(Function(r)=> (CInt(r.ToString) + 1)ToString)
                                     .ToArray
        return String.Join( ',', indexArray)
    End Function
    



    * 注意:这些是我写过的 VB 的第一行,所以我可能犯了一个基本错误(尤其是在 linq/lambda 方面),但重点就在那里。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-11-26
      • 2017-02-25
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 2011-04-05
      • 1970-01-01
      相关资源
      最近更新 更多