У меня есть приложение MonoTouch, которое выполняет HTTP POST с файлом 3,5 МБ, и оно очень нестабильно на основных платформах, на которых я тестирую (iPhone 3G с OS 3.1.2 и iPhone 4 с OS 4.2.1).Я опишу, что я делаю здесь, и, может быть, кто-то может сказать мне, если я делаю что-то не так.
Чтобы исключить остальную часть моего приложения, я сократил это до крошечногопример приложения.Приложение представляет собой iPhone OpenGL Project, и оно делает только это:
- При запуске выделите 6 МБ памяти в 30 000 блоков.Это моделирует использование памяти моего приложения.
- Считать файл 3,5 МБ в память.
- Создать тему для публикации данных.(Создайте объект WebRequest, используйте GetRequestStream () и запишите данные размером 3,5 МБ).
- Когда основной поток обнаружит, что поток публикации завершен, перейдите к шагу 2. и повторите.
Кроме того, для каждого кадра я выделяю 0-100k, чтобы смоделировать приложение, выполняющее что-либо.Я не храню никаких ссылок на эти данные, поэтому они должны собирать мусор.
Результат iPhone 3G: Приложение получает от 6 до 8 загрузок, а затем ОС убивает его.Журнала сбоев нет, но есть журнал LowMemory, показывающий, что приложение было отброшено.
iPhone 4 Результат: При 11-й загрузке возникает ошибка Mprotect.
Несколько точек данных:
- Инструменты НЕ отображаютсяпамять продолжает увеличиваться по мере того, как приложение продолжает загружаться.
- Приборы не показывают значительных утечек (возможно, всего 1 килобайт).
- Не имеет значения, пишу ли я данные поста в 64kкуски или все сразу с одним вызовом Stream.Write ().
- Не имеет значения, жду ли я ответа (HttpWebRequest.HaveResponse) или нет перед началом следующей загрузки.
- Не имеет значения, являются ли данные POST действительными.Я попытался использовать действительные данные POST и попытался отправить 3 МБ нулей.
- Если приложение не распределяет какие-либо данные в каждом кадре, потребуется больше времени для исчерпания памяти (но, как упоминалось ранее,память, на которую я выделяю каждый кадр, не указывается после кадра, на который он был выделен, поэтому он должен быть удален GC).
Если у кого-то нет идей, я подамошибка в Novell, но я хотел посмотреть, делаю ли я сначала что-то не так.
Если кому-то понадобится полный пример приложения, я могу его предоставить, но я вставил содержимое моего EAGLView.cs ниже.
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;
}
}