https://bugzilla.novell.com/show_bug.cgi?id=684316
https://bugzilla.novell.com/show_bug.cgi?id=684316#c0 Summary: WebRequest consistently dies with memory leak (iPhone3G) or Mprotect crash (iPhone4) Classification: Mono Product: MonoTouch Version: unspecified Platform: iPhone OS/Version: All Status: NEW Severity: Critical Priority: P5 - None Component: Runtime AssignedTo: [email protected] ReportedBy: [email protected] QAContact: [email protected] Found By: --- Blocker: --- User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16 I've got a MonoTouch app that does a 3.5MB HTTP POST. It seems to leak memory every time I upload. On an iPhone 3G (OS 3.1.2), the app is jettisoned with a LowMemory report after 6-10 uploads. On an iPhone 4 (OS 4.2.1), the app gets an Mprotect error after about 14 uploads. This is a big showstopper issue for our app because uploading Facebook videos is a major feature for us. A few data points: - Instruments does NOT show the memory increasing as the app continues to upload. - Instruments doesn't show any significant leaks (maybe 1 kilobyte total). - It doesn't matter whether I write the post data in 8k chunks, 64k chunks, or all at once with one Stream.Write() call. - It doesn't matter whether I wait for a response (HttpWebRequest.HaveResponse) or not before starting the next upload. - It doesn't matter if the POST data is even valid. I've tried using valid POST data and I've tried sending 3MB of zeros. - It doesn't matter if I use the async versions of the WebRequest calls from my main thread vs creating a thread of my own and using the synchronous versions of the WebRrequest and stream calls. - If the app is not allocating any data each frame (in SimulateAppAllocations), then the app still dies eventually but it takes longer to run out of memory. (BUT: as mentioned before, the memory that I'm allocating each frame is not referenced after the frame it was allocated on, so it should always be available for the garbage collector to cleanup). Reproducible: Always Steps to Reproduce: 1. Create a new MonoTouch iPhone OpenGL ES project. 2. Paste the following code into the EAGLView class and call UpdateCrashTest() from OnRenderFrame(). (Note that I've also attached the complete EAGLView.cs file to this bug report). void UpdateCrashTest() { SimulateAppAllocations(); UpdatePost(); } AsyncHttpPost m_Post; int m_nPosts = 1; void UpdatePost() { if ( m_Post == null || m_Post.PostStatus != AsyncHttpPostStatus.InProgress ) { System.Console.WriteLine( string.Format( "Starting post {0}", m_nPosts++ ) ); m_Post = new AsyncHttpPost( "https://api-video.facebook.com/restserver.php", "multipart/form-data; boundary=" + "8cdbcdf18ab6640" ); } } // // Stuff to randomly allocate in order to simulate a normal app's behavior. // Random m_Random = new Random(0); List< byte [] > m_Allocations; List< byte[] > m_InitialAllocations; void SimulateAppAllocations() { // First time through, allocate a bunch of data that the app would allocate. // We'll keep our pointers to this stuff so it doesn't get garbage collected. if ( m_InitialAllocations == null ) { m_InitialAllocations = new List<byte[]>(); int nInitialBytes = 6 * 1024 * 1024; int nBlockSize = 30000; for ( int nCurBytes = 0; nCurBytes < nInitialBytes; nCurBytes += nBlockSize ) { m_InitialAllocations.Add( new byte[nBlockSize] ); } } // Also, each frame, allocate up to 100k of data. We don't hang onto these pointers after the next // frame so it should get garbage collected. m_Allocations = new List<byte[]>(); for ( int i=0; i < 10; i++ ) { int nAllocationSize = m_Random.Next( 10000 ) + 10; m_Allocations.Add( new byte[nAllocationSize] ); } } } public enum AsyncHttpPostStatus { InProgress, Success, Fail } public class AsyncHttpPost { public AsyncHttpPost( string sURL, string sContentType ) { m_PostStatus = AsyncHttpPostStatus.InProgress; m_sContentType = sContentType; m_sURL = sURL; m_UploadThread = new Thread( new ThreadStart( UploadThread ) ); m_UploadThread.Start(); } void UploadThread() { using ( MonoTouch.Foundation.NSAutoreleasePool pool = new MonoTouch.Foundation.NSAutoreleasePool() ) { try { HttpWebRequest request = WebRequest.Create( m_sURL ) as HttpWebRequest; request.Method = "POST"; request.ContentType = m_sContentType; request.CachePolicy = new System.Net.Cache.RequestCachePolicy( System.Net.Cache.RequestCacheLevel.NoCacheNoStore ); int nBytesToWrite = 1024 * 1024 * 3; // m_PostData.Length; request.ContentLength = nBytesToWrite; // Write the post data. using ( Stream stream = request.GetRequestStream() ) { int nBlockSize = 1024 * 8; for ( int nBytesWritten=0; nBytesWritten < nBytesToWrite; nBytesWritten += nBlockSize ) { int nBytes = Math.Min( nBytesToWrite - nBytesWritten, nBlockSize ); byte [] bytes = new byte[nBytes]; stream.Write( bytes, 0, bytes.Length ); } stream.Close(); } // Wait for the response. while ( !request.HaveResponse ) { MonoTouch.Foundation.NSThread.SleepFor( 0.1f ); } System.Console.WriteLine( "Finished!" ); // Finished! m_PostStatus = AsyncHttpPostStatus.Success; } catch ( System.Exception e ) { System.Console.WriteLine( "Error in AsyncHttpPost.UploadThread:\n" + e.Message ); m_PostStatus = AsyncHttpPostStatus.Fail; } } } public AsyncHttpPostStatus PostStatus { get { return m_PostStatus; } } Thread m_UploadThread; AsyncHttpPostStatus m_PostStatus; string m_sContentType; string m_sURL; } Actual Results: iPhone 3G Result: The app gets through 6 to 10 uploads and then the OS kills it. There is no crash log, but there is a LowMemory log showing that the app was jettisoned. iPhone 4 Result: It gets an Mprotect error around the 11th upload. Expected Results: Since we're not keeping references to old objects here, the app should run forever without crashing or running out of memory. -- Configure bugmail: https://bugzilla.novell.com/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the QA contact for the bug. _______________________________________________ mono-bugs maillist - [email protected] http://lists.ximian.com/mailman/listinfo/mono-bugs
