内存问题在VB .NET中创建位图 [英] Memory issues creating a bitmap in 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屋!