2011-12-16 49 views
14

Tôi đã tạo một trang web nhận bitmap được mã hóa base64 qua Websocket và sau đó vẽ chúng vào canvas. Nó hoạt động hoàn hảo. Ngoại trừ, việc sử dụng bộ nhớ của trình duyệt (cho dù Firefox, Chrome hoặc Safari) tăng lên với mỗi hình ảnh và không bao giờ giảm xuống. Vì vậy, phải có một rò rỉ bộ nhớ trong mã của tôi hoặc một số lỗi khác. Nếu tôi nhận xét cuộc gọi đến context.drawImage, rò rỉ bộ nhớ không xảy ra (nhưng sau đó tất nhiên hình ảnh không bao giờ được vẽ). Dưới đây là các đoạn trích từ trang web của tôi. Bất kỳ trợ giúp được đánh giá cao. Cảm ơn!Rò rỉ dữ liệu bị rò rỉ trong Safari (là: Rò rỉ bộ nhớ với canvas HTML5)

// global variables 
var canvas; 
var context; 

... 

ws.onmessage = function(evt) 
{ 
    var received_msg = evt.data; 
    var display_image = new Image(); 
    display_image.onload = function() 
    { 
     context.drawImage(this, 0, 0); 
    } 
    display_image.src = 'data:image/bmp;base64,'+received_msg; 
} 

... 

canvas=document.getElementById('ImageCanvas'); 
context=canvas.getContext('2d'); 

... 

<canvas id="ImageCanvas" width="430" height="330"></canvas> 

CẬP NHẬT 12/19/2011

tôi có thể làm việc xung quanh vấn đề này bằng cách tạo động/phá hủy vải mỗi 100 hình ảnh hoặc lâu hơn với createElement/appendChild và removeChild. Sau đó, tôi không còn gặp vấn đề về bộ nhớ với Firefox và Chrome nữa.

Tuy nhiên, Safari vẫn gặp vấn đề về sử dụng bộ nhớ, nhưng tôi cho rằng đó là vấn đề khác, không liên quan đến Canvas. Dường như có vấn đề với việc thay đổi liên tục "src" của hình ảnh trong Safari, như thể nó sẽ không bao giờ giải phóng bộ nhớ này.

display_image.src = 'data:image/bmp;base64,'+received_msg; 

Đây là cùng một vấn đề được mô tả trên trang web sau đây: http://waldheinz.de/2010/06/webkit-leaks-data-uris/


CẬP NHẬT 12/21/2011

Tôi đã hy vọng để có được xung quanh vấn đề này bằng cách chuyển đổi Safari base64 nhận của tôi string to a blob (với hàm "dataURItoBlob" mà tôi tìm thấy trên trang web này) và quay lại URL có window.URL.createObjectURL, đặt hình ảnh của tôi src thành URL này và sau đó giải phóng bộ nhớ bằng cách gọi window.URL. revokeObjectURL. Tôi nhận được tất cả điều này làm việc, và Chrome và Firefox hiển thị hình ảnh một cách chính xác. Thật không may, Safari dường như không có hỗ trợ cho BlobBuilder, vì vậy nó không phải là một giải pháp tôi có thể sử dụng. Điều này thật kỳ lạ, vì nhiều nơi bao gồm cả trạng thái cuốn sách "Ứng dụng HTML5 lập trình" của O'Reilly mà BlobBuilder được hỗ trợ trong Safari/WebKit Nightly Builds. Tôi đã tải xuống bản dựng Windows mới nhất từ ​​http://nightly.webkit.org/ và chạy WebKit.exe nhưng BlobBuilder và WebKitBlobBuilder vẫn chưa được xác định.


CẬP NHẬT 01/03/2012

Ok, tôi cuối cùng đã cố định này bằng cách giải mã các dữ liệu URI chuỗi base64 mã hóa với atob() và sau đó tạo ra một mảng dữ liệu pixel và viết nó vào khung với putImageData (xem http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/). Làm theo cách này (trái với việc liên tục sửa đổi "src" của một hình ảnh và gọi drawImage trong hàm onload), tôi không còn thấy rò rỉ bộ nhớ trong Safari hoặc bất kỳ trình duyệt nào.

+0

những gì sẽ xảy ra nếu bạn thêm một cuộc gọi clearRect trước khi vẽ các hình ảnh, hoặc nếu bạn sử dụng thủ thuật thiết lập lại của thiết lập chiều rộng cho chính nó? (từ http://stackoverflow.com/questions/2142535/how-to-clear-the-canvas-for-redrawing) – Mikeb

+0

Tôi đã thử context.clearRect (0, 0, canvas.width, canvas.height); trước drawImage nhưng rò rỉ bộ nhớ vẫn xảy ra. – bglaudel

+0

Bạn có chắc chắn nó không chỉ lưu trữ hình ảnh chỉ trong trường hợp bạn quay trở lại trang để nó có thể tải nó từ bộ nhớ? Có thể là tối ưu hóa thời gian chạy. –

Trả lời

0

bạn có thể vẽ hình ảnh nhiều lần hơn bạn mong đợi. thử thêm một bộ đếm và xuất số đó vào một cảnh báo hoặc một số div trong trang để xem số lần hình ảnh đang được vẽ.

3

Nếu không có mã làm việc thực tế, chúng tôi chỉ có thể suy đoán là tại sao.

Nếu bạn gửi cùng một hình ảnh nhiều lần, bạn sẽ tạo một hình ảnh mới mỗi lần. Thật tệ.Bạn muốn làm một cái gì đó như thế này:

var images = {}; // a map of all the images 

ws.onmessage = function(evt) 
{ 
    var received_msg = evt.data; 
    var display_image; 
    var src = 'data:image/bmp;base64,'+received_msg; 
    // We've got two distinct scenarios here for images coming over the line: 
    if (images[src] !== undefined) { 
     // Image has come over before and therefore already been created, 
     // so don't make a new one! 
     display_image = images[src]; 
     display_image.onload = function() { 
      context.drawImage(this, 0, 0); 
     } 
    } else { 
     // Never before seen image, make a new Image() 
     display_image = new Image(); 
     display_image.onload = function() { 
      context.drawImage(this, 0, 0); 
     } 
     display_image.src = src; 
     images[src] = display_image; // save it for reuse 
    } 
} 

Có nhiều cách hiệu quả hơn để viết rằng (tôi sao chép mã onload ví dụ, và tôi không kiểm tra để xem nếu một hình ảnh đã được đầy đủ). Tôi sẽ để lại những phần đó cho bạn, bạn sẽ có được ý tưởng.

+0

Mỗi hình ảnh là duy nhất. – bglaudel

+0

Nếu mỗi hình ảnh là duy nhất và mức sử dụng bộ nhớ tăng lên khi bạn thực hiện mỗi HTMLImageElement mới ('hình ảnh mới()'), điều đó chỉ bình thường! –

+0

Vậy tại sao mức sử dụng bộ nhớ vẫn ở mức nếu tôi nhận xét cuộc gọi đến drawImage? – bglaudel

0

Điều đó rất thú vị. Đây là giá trị báo cáo như là một lỗi cho các nhà cung cấp trình duyệt khác nhau (cảm giác của tôi là nó không nên xảy ra). Bạn có thể trả lời dọc theo dòng "Đừng làm điều đó, thay vì làm như vậy và như vậy" nhưng ít nhất thì bạn sẽ biết câu trả lời đúng và có một điều thú vị để viết lên cho một bài đăng blog (nhiều người hơn chắc chắn sẽ chạy vào vấn đề này).

Một điều cần thử là xóa hình ảnh src (và trình xử lý tải) ngay sau khi gọi đến drawImage. Nó có thể không giải phóng tất cả bộ nhớ nhưng nó có thể nhận được hầu hết nó trở lại.

Nếu điều đó không hiệu quả, bạn luôn có thể tạo một nhóm các đối tượng hình ảnh và sử dụng lại chúng khi chúng đã vẽ lên khung vẽ. Đó là một rắc rối bởi vì bạn sẽ phải theo dõi trạng thái của các đối tượng đó và cũng đặt hồ bơi của bạn thành một kích thước thích hợp (hoặc làm cho nó phát triển/thu nhỏ dựa trên lưu lượng truy cập).

Vui lòng báo cáo lại kết quả của bạn. Tôi rất quan tâm bởi vì tôi sử dụng một kỹ thuật tương tự cho một trong các mã hóa chặt chẽPNG trong noVNC (và tôi chắc chắn rằng những người khác sẽ được quan tâm quá).

1

Tôi không tin đây là lỗi. Vấn đề có vẻ là các hình ảnh được xếp chồng lên nhau. Vì vậy, để xóa bộ nhớ, bạn cần sử dụng clearRect() để xóa canvas của bạn trước khi vẽ hình ảnh mới trong đó.

ctx.clearRect (0, 0, canvas.width, canvas.height);

How to clear your canvas matters

+0

Tôi nhận thấy rằng vì lý do nào đó chiều cao canvas dường như là sai trong trường hợp của tôi. Sử dụng chiều cao hình ảnh là "canvas.height" trong mẫu mã có vẻ như là lừa. – Stitch10925