内存问题在VB .NET中创建位图 [英] Memory issues creating a bitmap in VB .NET

查看:130
本文介绍了内存问题在VB .NET中创建位图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


我的应用程序读取文件(通常是纯文本),然后压缩它,然后根据压缩数据创建位图。

文件大小约为30 MB的文本(大约15 MB压缩),它的工作方式与预期一致。

40 MB的文本(20 MB压缩)我有时会遇到异常在创建新位图时。每次都以同样的方式出现错误。



来源是30 MB文本文件,压缩到大约15 MB

图像高度2943

图片宽度5232

pixels = 15397776

无错误



来源是40MB文本文件,压缩到大约20 MB

ImageHeight 3402整数

ImageWidth 6048整数

pixels = 20575296

违规行 - MainBitmap =新位图(ImageWidth,ImageHeight)

System.ArgumentException未处理

HResult = -2147024809

消息=参数不是有效。

来源= System.Drawing



用户首先打开文本文件或创建/输入密码。一旦完成,用户选择加密并开始流程:

1 - 使用GZIP或Deflate压缩将文本文件内容(存储为字节数组)压缩为另一个字节数组。

2 - 压缩的数组大小转换为位图宽度和高度,用户选择的近似宽高比(16:9,4:3,1:1),空位图是创建

3 - 对于数组中的每个可能字节(0..255),使用字符的SHA哈希和密码生成多个唯一颜色并将其添加到(颜色)列表中以一种奇特的方式连接起来。

4 - 对于文本文件内容中的每个字节,位图像素被设置为与该字节对应的颜色之一。

5 - 完成后,用户可以将位图保存为.png文件(无损)

第二步是异常发生的地方



位图在Form级别声明为Bitmap,然后在用户后设置为新位图选择加密一堆文本。此时内存配置文件详细信息显示50 MB阵列为52 MB,压缩为25.内存使用率在文本加载时峰值非常高(900 MB),然后恢复到80 MB左右并保持在那里。它在异常之前上升了一小部分。

原始数组是文本文件的大小,使用File.ReadAllBytes。

压缩数组大约是一半原始数组的大小。

我有两个词典,每个词条有1024个条目(整数,颜色)和(颜色,整数)



我尝试过:



My application reads a file (normally plain text), then compresses it, then creates a bitmap from the compressed data.
Up to a file size of about 30 MB of text (approximately 15 MB compressed) it works just as expected.
At 40 MB of text (20 MB compressed) I sometimes get an exception when creating the new bitmap. Anything above that errors every time in the same way.

Source is 30 MB Text File, compressed to approximately 15 MB
Image Height 2943
Image Width 5232
pixels = 15397776
No Error

Source is 40MB Text file, compressed to approximately 20 MB
ImageHeight 3402 Integer
ImageWidth 6048 Integer
pixels = 20575296
Offending Line - MainBitmap = New Bitmap(ImageWidth, ImageHeight)
System.ArgumentException was unhandled
HResult=-2147024809
Message=Parameter is not valid.
Source=System.Drawing

The user starts by either opening a text file or creating / typing a password. Once both are done, the user selects "Encrypt" and the process begins:
1 - The text file contents (stored as an array of bytes) is compressed to another byte array using either GZIP or Deflate compression.
2 - The compressed array size is translated to a Bitmap Width and Height with approximate Aspect Ratios as the user chooses (16:9, 4:3, 1:1), and an empty bitmap is created
3 - For each possible byte in the array (0..255), multiple unique colors are generated and added to a list (of color), using the SHA Hash of the character and the password concatenated in a fancy manner.
4 - for each byte in the text file contents, the bitmap pixel is set to one of the colors that corresponds to that byte.
5 - When finished, the user can save the bitmap as a .png file (lossless)
Step two is where the exception occurs

The Bitmap is declared at Form level just as a Bitmap, then set as a new bitmap once the user chooses to encrypt a bunch of text. At that point The Memory Profile details show the 50 MB array as 52 MB and the compressed as 25. Memory usage spikes very high (900 MB) when the text is loaded, then settles back down to 80 MB or so and stays there. It rises a small amount just before the exception.
The "original" array is the size of the text file, using File.ReadAllBytes.
The compressed array is around half the size of the "original" array.
I have two Dictionaries that are 1024 entries each (Integer,Color) and (Color,Integer)

What I have tried:

<pre lang="text">

我将Forms应用程序转换为控制台应用程序,它通过一个100到200 MB的源文件轻轻一点,没有任何问题。



我也重写了表单应用程序中的加密部分并删除了每个大对象(位图除外)

输入文件永远不会被读入数组或字符串。输入文件是压缩文件以存档到压缩文件中。如果输入文件小于256K,则会显示,否则只显示第一行和最后一行使用流读取器读取行,同样没有大字符串。

压缩文件直接保存到磁盘作为临时文件,它永远不会进入数组。

加密时,一次一个字节是从压缩文件中二进制读取并处理的。

经过测试和确认在任何高达40 MiB的工作(通过解密)。

在50 MiB时,每次设置位图大小(无效参数)时都会出现相同的错误。

这不是一个渐进的错误,这意味着我可以尝试一个50 MB的文件,并得到一个错误,然后尝试1,2,5,10,20等MB文件没有错误,然后50再次,这些错误。



这是确切的代码,除了我用一个List替换了2个字典,它现在处理50 MB而不是60.在我的x64机器上它处理200但显示900 MB的内存使用。



I converted the Forms App to a Console App and it breezes through a 100 to 200 MB source file with no issues at all.

I also rewrote the encryption part in the Forms App and got rid of every "large object" (except the bitmap)
The input file never gets read into an array or string. The input file is compressed file to file into a compressed file. If the Input File is less than 256K, it gets displayed, otherwise only the first and last lines are displayed using a streamreader to read lines, again, no big strings.
That compressed file is directly saved to disk as a Temp File, it never gets into an array.
When encrypting, one byte at a time is binary read from the compressed file and processed.
Tested and confirmed working (by decrypting) on anything up to and including 40 MiB.
At 50 MiB, the same error occurs every time while setting the bitmap size (Invalid Parameter).
It's not a gradual error, meaning I can try a 50 MB file, and get an error, then try 1,2,5,10,20 etc MB files with no errors, then 50 again, which errors.

Here is the exact code except I have replaced 2 dictionaries with one List, it handles 50 MB now but not 60. On my x64 machine it handles 200 but shows 900 MB of memory used.

Option Strict On
Imports System.IO
Imports System.IO.Compression
Imports System.Security.Cryptography
Imports System.Text

Public Class Form1
    Private WithEvents TxtStatus As New TextBox
    Private WithEvents BtnTest As New Button
    Private WithEvents TxtEncryptKey As New TextBox
    Private Label1 As New Label
    Private WithEvents BtnLoadFile As New Button
    Private TxtPlain As New RichTextBox
    Private RNG As New Random
    Private CompressedFileName As String
    Private CompressedLength As Long
    Private InputFileName As String
    Private InputLength As Long
    Private ColorList As New List(Of Color)
    Private MainBitmap As Bitmap
    Private EncryptKey As String
    Private ImageWidth As Integer
    Private ImageHeight As Integer

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        AutoScaleMode = AutoScaleMode.Font
        ClientSize = New Size(604, 270)
        MinimumSize = Size
        With TxtStatus
            .Anchor = CType(((AnchorStyles.Bottom Or AnchorStyles.Left) Or AnchorStyles.Right), AnchorStyles)
            .Location = New Point(4, 224)
            .Multiline = True
            .Size = New Size(594, 37)
        End With
        With BtnTest
            .Location = New Point(206, 188)
            .Size = New Size(120, 23)
            .Text = "Test"
        End With
        With TxtEncryptKey
            .Anchor = CType(((AnchorStyles.Top Or AnchorStyles.Left) Or AnchorStyles.Right), AnchorStyles)
            .Location = New Point(80, 131)
            .Size = New Size(518, 22)
        End With
        With Label1
            .AutoSize = True
            .Location = New Point(3, 134)
            .Size = New Size(70, 14)
            .Text = "Password:"
        End With
        With BtnLoadFile
            .Location = New Point(80, 188)
            .Size = New Size(120, 23)
            .Text = "Load A File"
        End With
        With TxtPlain
            .Anchor = CType(((AnchorStyles.Top Or AnchorStyles.Left) Or AnchorStyles.Right), AnchorStyles)
            .Location = New Point(4, 2)
            .Size = New Size(594, 123)
        End With

        With Me
            .Controls.Add(TxtPlain)
            .Controls.Add(BtnLoadFile)
            .Controls.Add(BtnTest)
            .Controls.Add(TxtStatus)
            .Controls.Add(Label1)
            .Controls.Add(TxtEncryptKey)
            .Font = New Font("Consolas", 9.0!, FontStyle.Regular, GraphicsUnit.Point, CType(0, Byte))
            .Text = "ONLY FOR TESTING " & " " & Application.ProductVersion & " TESTING ONLY"
        End With

    End Sub

    Private Function GetSHA1Color(ByVal Input As String) As Color
        Dim SHA1Hasher As New SHA1Managed()
        Dim Hash As Byte() = SHA1Hasher.ComputeHash(Encoding.Default.GetBytes(Input))
        Return Color.FromArgb(255, Hash(0), Hash(1), Hash(2))
    End Function

    Private Function FillColorList(EncryptKey As String) As Boolean
        ColorList.Clear()
        Dim ColorToAdd As Color = Nothing
        For CharacterValue As Integer = 0 To 255
            ColorToAdd = GetSHA1Color(EncryptKey & Chr(CharacterValue))
            If ColorList.Contains(ColorToAdd) Then
                Return False
            Else
                ColorList.Add(ColorToAdd)
            End If
        Next
        Return True
    End Function

    Private Sub CompressFileToFile(InFile As String, Outfile As String)
        Dim InFileInfo As FileInfo = New FileInfo(InFile)
        Dim InFileLength As Long = InFileInfo.Length
        Dim Bytesread As Long = 0
        Dim BytesRemaining As Long = InFileLength
        Dim BufferSize As Long = InFileLength \ 100
        Dim BytesToRead As Long = BufferSize
        Dim buffer(CInt(BufferSize - 1)) As Byte
        Dim OutFileinfo As New FileInfo(Outfile)
        Me.Cursor = Cursors.WaitCursor
        Try
            Using originalFileStream As FileStream = InFileInfo.OpenRead()
                Using compressedFileStream As FileStream = File.Create(Outfile)
                    Using compressionStream As New GZipStream(compressedFileStream, CompressionMode.Compress)
                        Do While BytesRemaining > 0
                            originalFileStream.Read(buffer, 0, CInt(BufferSize))
                            compressionStream.Write(buffer, 0, CInt(BufferSize))
                            Bytesread += BufferSize
                            TxtStatus.Text = "Compressing - " & ((Bytesread * 100) \ InFileLength).ToString("000") & "%"
                            TxtStatus.Refresh()
                            BytesRemaining -= BufferSize
                            If BytesRemaining < BufferSize Then BufferSize = BytesRemaining
                        Loop
                    End Using
                End Using
            End Using
        Catch ex As Exception
            TxtStatus.Text = "Error Compressing Input File " & ex.Message
            If File.Exists(CompressedFileName) Then File.Delete(CompressedFileName)
        End Try
        Me.Cursor = Cursors.Default
    End Sub

    Private Sub CalcSize(InputString As String)
        CompressedFileName = Path.GetTempFileName
        CompressFileToFile(InputString, CompressedFileName)
        If Not File.Exists(CompressedFileName) Then
            InputFileName = ""
            CompressedFileName = ""
            MainBitmap = Nothing
            Exit Sub
        End If
        Dim FileLength As Integer = CInt(My.Computer.FileSystem.GetFileInfo(CompressedFileName).Length)
        CompressedLength = FileLength
        Dim Temp As Long = 0
        For N As Integer = 1 To 100000
            ImageWidth = N
            ImageHeight = N
            Temp = ImageWidth * ImageHeight
            If Temp > FileLength Then
                Exit For
            End If
        Next
        TxtStatus.Text = "Compressed Length is " & FileLength.ToString & ", the bitmap will be " & ImageWidth.ToString & " by " & ImageHeight.ToString & vbNewLine
        If Not MainBitmap Is Nothing Then
            MainBitmap.Dispose()
        End If
        Try
            MainBitmap = New Bitmap(ImageWidth, ImageHeight, Imaging.PixelFormat.Format32bppArgb)
        Catch ex As Exception
            TxtStatus.Text = ImageWidth.ToString & "x" & ImageHeight.ToString & " Bitmap Creation Failed - " & ex.Message & vbNewLine & ex.StackTrace
            MainBitmap = Nothing
        End Try
    End Sub

    Private Sub BtnTest_Click(sender As System.Object, e As System.EventArgs) Handles BtnTest.Click
        If EncryptKey = "" Then
            TxtStatus.Text = "A Password is required."
            Exit Sub
        End If

        If Not File.Exists(InputFileName) OrElse My.Computer.FileSystem.GetFileInfo(InputFileName).Length = 0 Then
            TxtStatus.Text = "Some Plain text is required"
            Exit Sub
        End If

        If Not FillColorList(EncryptKey) Then
            MessageBox.Show("The Password you chose : " & EncryptKey & vbNewLine &
                            "Cannot be used for testing." & vbNewLine &
                            "Choose a different password.", "Cannot test with this password", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Exit Sub
        End If

        TxtStatus.Text = "Preparing .........."
        TxtStatus.Refresh()
        CalcSize(InputFileName)
        If File.Exists(CompressedFileName) Then File.Delete(CompressedFileName)
        If MainBitmap Is Nothing Then
            Exit Sub
        Else
            TxtStatus.Text = ImageWidth.ToString & "x" & ImageHeight.ToString & " Bitmap Creation Succeeded"
        End If

    End Sub

    Private Sub TxtKey_TextChanged(sender As System.Object, e As System.EventArgs) Handles TxtEncryptKey.TextChanged
        EncryptKey = TxtEncryptKey.Text
        BtnTest.Enabled = EncryptKey.Length > 0
    End Sub

    Private Sub BtnLoadFile_Click(sender As System.Object, e As System.EventArgs) Handles BtnLoadFile.Click
        Dim FileSize As Long = 0
        Dim TempString As String = ""
        Using OFD As New OpenFileDialog
            OFD.Title = "Find the text file you wish to encrypt"
            OFD.Filter = "Text Files (*.txt)|*.txt"
            If OFD.ShowDialog = Windows.Forms.DialogResult.OK Then
                InputFileName = OFD.FileName
                FileSize = My.Computer.FileSystem.GetFileInfo(InputFileName).Length
                InputLength = FileSize
                Try
                    If FileSize > 64 * 1024 Then
                        Dim firstLine As String = Nothing ' or String.Empty
                        Dim lastLine As String = Nothing ' same here

                        Using reader As New StreamReader(InputFileName)
                            If Not reader.EndOfStream Then firstLine = reader.ReadLine
                            Do Until reader.EndOfStream
                                lastLine = reader.ReadLine
                            Loop
                        End Using
                        TxtPlain.Text = firstLine &
                                        vbNewLine & vbNewLine & "Text Redacted" & vbNewLine & vbNewLine &
                                        lastLine
                    Else
                        TxtPlain.Text = File.ReadAllText(InputFileName, Encoding.Default)
                    End If
                    TxtStatus.Text = "Read " & FileSize.ToString & " Characters"
                Catch ex As Exception
                    TxtStatus.Text = "Error reading file - " & ex.Message
                    InputFileName = ""
                End Try
            End If
        End Using
    End Sub
End Class

推荐答案

简单,你不能使Bitmap对象变大,特别是如果你在完成使用时忘记在需要它的对象上调用Dispose他们。对于绘图对象尤其如此。



阅读这个 [ ^ ],
Simple, you can't make a Bitmap object that big, especially so if you're forgetting to call Dispose on the object that require it when you're done using them. This is especially true of drawing objects.

Read this[^],


这篇关于内存问题在VB .NET中创建位图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆