Image物件的記憶體佔用問題。
前幾天維護的程式發生了記憶體不足的問題,經檢視,是由於使用者一次載入了15張圖片,每張圖片的大小都接近1M,使用者把圖片修改後,程式可以正常處理了。
不過又想了一下,15張JPG圖片的大小 加起來也不過是最大15M而已,也不至於記憶體不夠呀!
於是檢查程式碼:
debug時發現只要執完 orginalimg = (Bitmap)Image.FromStream(picStream); 這一句,記憶體暴增100多M。然後圖片的大小隻有700多K,開始時,以為是Memorystream沒有釋放資源,弄了很久,也沒有解決問題。
最後看到網上有人遇到類似的問題,有人提醒說有可能是Image的問題。
然後加上了orginalimg.Dispose()這句,發現果然,只要這一句一執行,點用的100多M記憶體就釋放了。
可是由於後面還需要用到這個image,這時不能釋放。因為介面會顯示載入的所有圖片的小圖。
然後決定重點查了一下: 為什麼一個700多K的JPG讀到一個IMG物件後,會變成上白兆?
經過一翻Google後,發現在IMage的記憶體佔用大小的計算方法如下:
如果是32位,確實是Width * Height * 4
實際上應該是Height *(((Width * 32) + 31) / 32) * 4
如果是24位的化,就不一定是Width * Height * 3了,
而是Height *(((Width * 24) + 31) / 32) * 4
Note: 上面的Height 和Weight是圖片的原始大小。可以根據Image的這兩個屬性值來檢視具體的大小。
查看了一下我讀入的圖片的大小是:Height= 6800, Width = 4400. 代入上面的公式一算,果然是100M多(另上面公式算出來的是位元組數,除以1024*1024可以得到多少M)
PS:所以我讀的圖片是JPG格式, jpg是壓縮格式的點陣圖,它本身不是真正的原始點陣圖,進入記憶體後,要經過一定的演算法的才能成為原始點陣圖,也就是說,記憶體中的才是真正原始點陣圖的大小,這時,它是未壓縮的,並以純二進位制表示的,所以佔用記憶體超出檔案的大小。
由於介面顯示的是縮圖,所以我只需要把圖片尺寸改小,就應該能正常顯示了,
double scale = 0.1
originalIMG = orginalimg.GetThumbnailImage(Convert.ToInt32(orginalimg.Size.Width * scale), Convert.ToInt32(orginalimg.Size.Height * scale), null, IntPtr.Zero)
完成後再呼叫orginalimg.Dispose()釋放那100M多記憶體。這樣,當用戶讀一系列的圖片進來時,就可以正常顯示了。
(另:如果需要單獨看某一張圖片用原始尺寸,可以用一個變數把圖片按位元組讀進來,然後單獨顯示時,再讀到IMG,這樣程式也可以正常顯示,因為也就是100多M,而不是多個100M)。