MonoTouch-WebRequest内存泄漏和崩溃? [英] MonoTouch - WebRequest memory leak and crash?

查看:52
本文介绍了MonoTouch-WebRequest内存泄漏和崩溃?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个MonoTouch应用程序,该应用程序使用3.5MB的文件执行HTTP POST,并且在我测试的主要平台(操作系统为3.1.2的iPhone 3G和操作系统4.2.1的iPhone 4)上非常不稳定).我会在这里描述我在做什么,也许有人可以告诉我我做错了什么.

I've got a MonoTouch app that does an HTTP POST with a 3.5MB file, and it is very unstable on the primary platforms that I test on (iPhone 3G with OS 3.1.2 and iPhone 4 with OS 4.2.1). I'll describe what I'm doing here and maybe someone can tell me if I'm doing something wrong.

为了排除我的应用程序的其余部分,我将其缩减为一个很小的示例应用程序.该应用程序是一个iPhone OpenGL项目,它仅执行以下操作:

In order to rule out the rest of my app, I've whittled this down to a tiny sample app. The app is an iPhone OpenGL Project and it does only this:

  1. 启动时,以30k块分配6MB内存.这模拟了我的应用程序的内存使用情况.
  2. 将3.5MB的文件读取到内存中.
  3. 创建一个线程来发布数据. (制作一个WebRequest对象,使用GetRequestStream()并写入3.5MB数据).
  4. 当主线程检测到发布线程已完成时,转到步骤2并重复.

此外,每帧我分配0-100k来模拟应用程序执行的操作.我没有保留对此数据的任何引用,因此应该将其收集为垃圾.

Also, each frame, I allocate 0-100k to simulate the app doing something. I don't keep any references to this data so it should be getting garbage collected.

iPhone 3G结果:该应用完成了6到8次上传,然后操作系统将其杀死.没有崩溃日志,但是有一个LowMemory日志显示该应用已被抛弃.

iPhone 3G Result: The app gets through 6 to 8 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结果::在第11次上传前后出现Mprotect错误.

iPhone 4 Result: It gets an Mprotect error around the 11th upload.

一些数据点:

  • 随着应用程序继续上传,乐器不会显示内存增加.
  • 仪器未显示任何重大泄漏(可能总计1 KB).
  • 通过一个Stream.Write()调用将数据写入64k块还是一次全部写入都没关系.
  • 在开始下一次上传之前,我是否等待响应(HttpWebRequest.HaveResponse)都没有关系.
  • POST数据是否有效都没有关系.我尝试使用有效的POST数据,并尝试发送3MB的零.
  • 如果应用未在每个帧上分配任何数据,则需要花费更长的时间来耗尽内存(但是如前所述,分配帧后未引用我正在分配每个帧的内存,因此应该由GC来收集.)

如果没有任何想法,我将向Novell提交错误,但我想先看看我在做错什么.

If nobody has any ideas, I'll file a bug with Novell, but I wanted to see if I'm doing something wrong here first.

如果有人想要完整的示例应用程序,我可以提供它,但是我在下面粘贴了我的EAGLView.cs的内容.

If anyone wants the full sample app, I can provide it, but I've pasted the contents of my EAGLView.cs below.

using System;
using System.Net;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using OpenTK.Platform.iPhoneOS;
using MonoTouch.CoreAnimation;
using OpenTK;
using OpenTK.Graphics.ES11;
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
using MonoTouch.OpenGLES;

namespace CrashTest
{
    public partial class EAGLView : iPhoneOSGameView
    {
        [Export("layerClass")]
        static Class LayerClass ()
        {
            return iPhoneOSGameView.GetLayerClass ();
        }

        [Export("initWithCoder:")]
        public EAGLView (NSCoder coder) : base(coder)
        {
            LayerRetainsBacking = false;
            LayerColorFormat = EAGLColorFormat.RGBA8;
            ContextRenderingApi = EAGLRenderingAPI.OpenGLES1;
        }

        protected override void ConfigureLayer (CAEAGLLayer eaglLayer)
        {
            eaglLayer.Opaque = true;
        }


        protected override void OnRenderFrame (FrameEventArgs e)
        {
            SimulateAppAllocations();
            UpdatePost();           

            base.OnRenderFrame (e);
            float[] squareVertices = { -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f };
            byte[] squareColors = { 255, 255, 0, 255, 0, 255, 255, 255, 0, 0,
            0, 0, 255, 0, 255, 255 };

            MakeCurrent ();
            GL.Viewport (0, 0, Size.Width, Size.Height);

            GL.MatrixMode (All.Projection);
            GL.LoadIdentity ();
            GL.Ortho (-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
            GL.MatrixMode (All.Modelview);
            GL.Rotate (3.0f, 0.0f, 0.0f, 1.0f);

            GL.ClearColor (0.5f, 0.5f, 0.5f, 1.0f);
            GL.Clear ((uint)All.ColorBufferBit);

            GL.VertexPointer (2, All.Float, 0, squareVertices);
            GL.EnableClientState (All.VertexArray);
            GL.ColorPointer (4, All.UnsignedByte, 0, squareColors);
            GL.EnableClientState (All.ColorArray);

            GL.DrawArrays (All.TriangleStrip, 0, 4);

            SwapBuffers ();
        }


        AsyncHttpPost m_Post;
        int m_nPosts = 1;

        byte[] LoadPostData()
        {
            // Just return 3MB of zeros. It doesn't matter whether this is valid POST data or not.
            return new byte[1024 * 1024 * 3];
        }

        void UpdatePost()
        {
            if ( m_Post == null || m_Post.PostStatus != AsyncHttpPostStatus.InProgress )
            {
                System.Console.WriteLine( string.Format( "Starting post {0}", m_nPosts++ ) );

                byte [] postData = LoadPostData();

                m_Post = new AsyncHttpPost( 
                    "https://api-video.facebook.com/restserver.php", 
                    "multipart/form-data; boundary=" + "8cdbcdf18ab6640",
                    postData );
            }
        }

        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.
            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] );
                }
            }

            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, byte [] postData )
        {
            m_PostData = postData;
            m_PostStatus = AsyncHttpPostStatus.InProgress;
            m_sContentType = sContentType;
            m_sURL = sURL;

            //UploadThread();
            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.ContentLength = m_PostData.Length;

                    // Write the post data.
                    using ( Stream stream = request.GetRequestStream() )
                    {
                        stream.Write( m_PostData, 0, m_PostData.Length );
                        stream.Close();
                    }

                    System.Console.WriteLine( "Finished!" );

                    // We're done with the data now. Let it be garbage collected.
                    m_PostData = null;

                    // 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;

        // Queued to be handled in the main thread.
        byte [] m_PostData;

        AsyncHttpPostStatus m_PostStatus;
        string m_sContentType;
        string m_sURL;
    }
}

推荐答案

我认为您应该一次读取1 KB(或任意大小)的文件并将其写入Web请求.

I think you should read in your file 1 KB (or some arbitrary size) at a time and write it to the web request.

与此类似的代码:

byte[] buffer = new buffer[1024];
int bytesRead = 0;
using (FileStream fileStream = File.OpenRead("YourFile.txt"))
{
    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
    {
        httpPostStream.Write(buffer, 0, bytesRead);
    }
}

这是我的头上的想法,但我认为是对的.

This is off the top of my head, but I think it's right.

这样,当您不需要时,内存中就不会再有3MB的空闲空间了.我认为这样的技巧在iDevice(或其他设备)上比在台式机上更重要.

This way you don't have an extra 3MB floating around in memory when you don't really need to. I think tricks like this are even more important on iDevices (or other devices) than on the desktop.

也要测试缓冲区的大小,更大的缓冲区将使您的速度提高到一个点(我记得8KB相当不错).

Test the buffer size too, a larger buffer will get you better speeds up to a point (I remember 8KB being pretty good).

这篇关于MonoTouch-WebRequest内存泄漏和崩溃?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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