Ошибка памяти HTTPListener службы Windows - PullRequest
0 голосов
/ 30 апреля 2010

Я полный новичок в "лучших практиках" и т.д. написания любого кода. Я просто пишу это, если оно работает, зачем это исправлять.

Ну, такой способ работы - посадить меня в горячую воду. Я пишу простой сервис Windows для сервера одной веб-страницы. (Эта служба будет включена в другой проект, который отслеживает службы и некоторые папки в группе серверов.)

Моя проблема заключается в том, что всякий раз, когда запрос получен, использование памяти увеличивается на несколько К за запрос и продолжает увеличиваться при каждом запросе.

Теперь я обнаружил, что, поместив GC.Collect в микс, он останавливается на определенном числе, но я уверен, что он не предназначен для этого. Мне было интересно, если я что-то упускаю или не делаю, я должен освободить память.

Вот код:

Public Class SimpleWebService : Inherits ServiceBase
    'Set the values for the different event log types.
    Public Const EVENT_ERROR As Integer = 1
    Public Const EVENT_WARNING As Integer = 2
    Public Const EVENT_INFORMATION As Integer = 4
    Public listenerThread As Thread
    Dim HTTPListner As HttpListener
    Dim blnKeepAlive As Boolean = True

    Shared Sub Main()
        Dim ServicesToRun As ServiceBase()
        ServicesToRun = New ServiceBase() {New SimpleWebService()}
        ServiceBase.Run(ServicesToRun)
    End Sub

    Protected Overrides Sub OnStart(ByVal args As String())
        If Not HttpListener.IsSupported Then
            CreateEventLogEntry("Windows XP SP2, Server 2003, or higher is required to " & "use the HttpListener class.")
            Me.Stop()
        End If
        Try
            listenerThread = New Thread(AddressOf ListenForConnections)
            listenerThread.Start()
        Catch ex As Exception
            CreateEventLogEntry(ex.Message)
        End Try
    End Sub

    Protected Overrides Sub OnStop()
        blnKeepAlive = False
    End Sub

    Private Sub CreateEventLogEntry(ByRef strEventContent As String)
        Dim sSource As String
        Dim sLog As String
        sSource = "Service1"
        sLog = "Application"
        If Not EventLog.SourceExists(sSource) Then
            EventLog.CreateEventSource(sSource, sLog)
        End If
        Dim ELog As New EventLog(sLog, ".", sSource)
        ELog.WriteEntry(strEventContent)
    End Sub

    Public Sub ListenForConnections()
        HTTPListner = New HttpListener
        HTTPListner.Prefixes.Add("http://*:1986/")
        HTTPListner.Start()
        Do While blnKeepAlive
            Dim ctx As HttpListenerContext = HTTPListner.GetContext()
            Dim HandlerThread As Thread = New Thread(AddressOf ProcessRequest)
            HandlerThread.Start(ctx)
            HandlerThread = Nothing
        Loop
        HTTPListner.Stop()
    End Sub

    Private Sub ProcessRequest(ByVal ctx As HttpListenerContext)
        Dim sb As StringBuilder = New StringBuilder
        sb.Append("<html><body><h1>Test My Service</h1>")
        sb.Append("</body></html>")
        Dim buffer() As Byte = Encoding.UTF8.GetBytes(sb.ToString)
        ctx.Response.ContentLength64 = buffer.Length
        ctx.Response.OutputStream.Write(buffer, 0, buffer.Length)
        ctx.Response.OutputStream.Close()
        ctx.Response.Close()
        sb = Nothing
        buffer = Nothing
        ctx = Nothing
        'This line seems to keep the mem leak down
        'System.GC.Collect()
    End Sub
End Class

Пожалуйста, не стесняйтесь критиковать и разрывать код на части, но, пожалуйста, будьте добры. Я признал, что не склонен следовать лучшим практикам, когда дело доходит до кодирования.

1 Ответ

2 голосов
/ 30 апреля 2010

Вы правы, вы должны не делать это. Удалите вызов Collect () и дайте ему поработать неделю. Любая приличная книга .NET расскажет о том, как работает сборщик мусора и как он не немедленно освобождает память, когда вы устанавливаете объект в Nothing. Он не срабатывает, пока вы не потратите от 2 до 8 мегабайт. Это не утечка, а просто эффективное использование обильного ресурса.

Вы используете новый поток для каждого отдельного соединения, это довольно дорого и масштабируется очень плохо, когда вы получаете много соединений. Попробуйте вместо этого использовать ThreadPool.QueueUserWorkItem. Потоки потоков очень дешевы, а их распределение и выполнение хорошо контролируются диспетчером потоков.

...