2014-10-16 62 views
2

我正在用C++/Qt開發下載服務器。我正面臨記憶日益增長的問題。在這裏,我分享示例服務器應用程序來演示該問題。服務器內存增長

當客戶端連接時,它開始每秒發送10Kb數據塊。當客戶端斷開時,套接字被刪除。

#include <QCoreApplication> 
#include <QtNetwork> 

class Client: public QObject 
{ 
    Q_OBJECT 
public: 
    Client(QSslSocket *sock) 
    { 
     this->timer = new QTimer(this); 
     this->timer->setInterval(1000); 
     connect(this->timer, SIGNAL(timeout()), this, SLOT(sendData())); 

     this->sock = sock; 
     connect(sock, SIGNAL(disconnected()), this, SIGNAL(disconnected()));   
     connect(sock, SIGNAL(encrypted()), this->timer, SLOT(start())); 
     connect(sock, SIGNAL(readyRead()), this, SLOT(readData())); 
    } 

    ~Client() 
    { 
     delete this->sock; 
    } 

    void start() 
    { 
     this->sock->startServerEncryption(); 
    } 

signals: 
    void disconnected(); 

private slots: 
    void sendData() 
    {    
     qDebug() << "sending data via socket: " << sock->socketDescriptor(); 

     if (this->sock->bytesToWrite()) 
      return; 

     QByteArray ba(10*1024, '1'); 
     this->sock->write(ba); 
    } 

    void readData() 
    { 
     this->sock->readAll(); 
    } 

private: 
    QSslSocket *sock; 
    QTimer *timer; 
}; // Client 


class Server: public QTcpServer 
{ 
    Q_OBJECT 
public: 
    Server() 
    {  
     this->totalConnected = 0; 
     this->instanceCounter = 0; 
    } 

protected: 
    virtual void incomingConnection(int d); 

private: 
    int totalConnected; 
    int instanceCounter; 

private slots: 
    void handleClientDisconnected(); 
    void handleDestroyed(); 
}; // Server 

void Server::incomingConnection(int d) 
{  
    QSslSocket *sock = new QSslSocket(this); 

    if (!sock->setSocketDescriptor(d)) 
    { 
     delete sock; 
     return; 
    } 

    ++this->instanceCounter; 

    qDebug() << "socket " << d << "connected, total: " << ++this->totalConnected; 

    sock->setLocalCertificate(":/ssl/resources/my.crt"); 
    sock->setPrivateKey(":/ssl/resources/my.key", QSsl::Rsa, QSsl::Pem, "my.pass"); 

    Client *client = new Client(sock); 
    connect(client, SIGNAL(disconnected()), this, SLOT(handleClientDisconnected())); 
    connect(client, SIGNAL(destroyed()), this, SLOT(handleDestroyed())); 

    client->start(); 
} 

void Server::handleClientDisconnected() 
{ 
    qDebug() << "client disconnected, total: " << --this->totalConnected; 
    sender()->deleteLater(); 
} 

void Server::handleDestroyed() 
{ 
    qDebug() << "destroyed: " << --this->instanceCounter; 
} 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    Server server; 
    if (server.listen(QHostAddress::Any, 563)) 
     qDebug() << "listen started"; 
    else qDebug() << "listen failed"; 

    return a.exec(); 
} 

#include "main.moc" 

有關於它的兩個問題:

1)爲什麼記憶不斷在下載成長?

我正在測試200-300連接。它在幾分鐘內達到了400 Mb並且不會停止。

在客戶端:: sendData我檢查this-> sock-> bytesToWrite()知道是否有什麼等待寫入。因此,在寫入所有內容之前,不會添加新數據。所有的數據塊都具有相同的大小,所以它不能爲新的數據分配更多的內存。

2)爲什麼不關閉所有連接都返回的內存?

雖然下載時使用的內存在客戶端斷開連接時斷開,但看起來並不是全部都返回。經過幾次建立200-700個連接的測試後,達到80 Mb,並且在根本沒有客戶端(客戶端對象全部被報告銷燬,實例計數器變爲零)時始終保持在該水平。

我在讀關於對象刪除和內存釋放的區別。據我瞭解,系統可能會爲未來的需求(某種優化)預留它。但是當別的東西需要時,它肯定必須返回這個記憶。我決定編寫一個分配大量內存(即數組)的小程序,以查看它是否會使系統兌換服務器使用的內存。它沒有。該程序崩潰,因爲沒有足夠的內存(它在服務器剛啓動時工作正常)。

所以看起來有什麼問題。我懷疑泄漏,但內存泄漏檢測器似乎沒有注意到任何嚴重問題。 Visual Leak Detector報告說沒有內存泄漏。 Valgrind報告了一些問題,但他們指的是Qt庫,我在讀它們只是虛驚一場,通常只是valgrind之後釋放內存的低級庫的副作用。無論如何,據報道丟失數據的總量與80 Mb相比非常小。

回答

0

內存分配器可以被配置爲將未使用的內存塊返回給系統,這是真的,但他們不會或物理上不能。

首先,您必須查看您的特定內存分配器,並查看它是否配置爲將內存返回給系統。這取決於你的操作系統和編譯器上,這兩者都不是你提供的信息,但是這太問題應該回答你:

Will malloc implementations return free-ed memory back to the system?

無論是能夠以取決於堆的碎片。只有完整的內存塊可以返回到系統,所以分散在整個堆中的微小分配將阻止這一點(儘管分配器通常試圖避免這種情況)。