2010-12-28 5 views
1

lang Zeit Benutzer, zum ersten Mal Poster ...Wie richte ich einen Thread richtig ein, der in Java gecodiert werden soll?

Wie auch immer, ich fragte mich, wie man einen erstellten Thread zu GC'd einrichten. Ich habe einen einfachen Multithread-Server erstellt, um Verbindungen vom Client zu akzeptieren, aber jedes Mal, wenn ein Client eine Verbindung herstellt, wächst die Speicherauslastung um etwa 80 k ... verständlich. Das Problem ist, dass ich nicht weiß, wie ich von diesem schnellen Wachstum zurückkommen kann, nachdem der Client und der Server die Verbindung getrennt haben.

Hier ist die Hauptklasse, die den Thread einrichtet:

public static void main(String[] args) { 

    try { 
    serverSock= new ServerSocket(9999); 
    } catch (IOException e1) { 
    // TODO Auto-generated catch block 
    e1.printStackTrace(); 
    } 

    while (true){ 
    try { 
    Thread t = new server(serverSock.accept()); 
    t.start(); 
    t = null; 
    } catch (IOException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } 
    } 

    } 
} 

Und hier ist der grundlegende Server-Thread:

public class server extends Thread { 
private Socket serverSocket = null; 
public static String command = null; 

public server(Socket serverSocket2) { 
    super("server"); 
    serverSocket = serverSocket2; 
} 


@Override 
public void run(){ 
    BufferedReader fromClient = null; 
    PrintStream toClient = null; 
    String line = null; 
    String[] user = new String[2]; 

    try { 
    serverSocket.setSoTimeout(10000); 
    fromClient = new BufferedReader(new InputStreamReader(serverSocket.getInputStream())); 
    toClient = new PrintStream(serverSocket.getOutputStream()); 
    System.out.println("Got connection"); 
      System.out.println("Terminating server"); 
    fromClient.close(); 
    toClient.close(); 
    serverSocket.close() 
     } catch (NumberFormatException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } catch (IOException e) { 
    System.out.println("Connection established, but timeout on" + 
    " message recieve may have occured"); 
    e.printStackTrace(); 
    } 
} 

Nun, wie genau würde ich erlaube diesem Thema nur ... sterben und weggehen, anstatt das Speicherleck zu verursachen, das gerade auftritt?

Auch ich bin neu in Java - egal Thread zu verwenden, also wenn es offensichtlich ist, bitte versuchen Sie, nett darüber zu sein. Vielen Dank für Ihre Hilfe!

+0

Das Setzen von 't' auf' null' hat keinerlei Wirkung, da der Block unmittelbar nach dieser Anweisung endet und 't' den Gültigkeitsbereich verlässt. @Pete: Was passiert, wenn Sie 't' auf' null' setzen, während der Thread läuft? Nichts wird dem Thread passieren. – Jesper

+0

Ich hatte den Eindruck, dass, wenn wir nicht einen Zeiger auf die Thread-Unterklasse behielten, es GC'd werden könnte, nachdem es außer Geltungsbereich (wie jedes andere Objekt) ging. Ich denke nicht? – Pete

Antwort

4

Ein GC läuft nur, wenn es nötig ist, es scheint nicht nötig zu sein, damit Objekte/Thread, die gereinigt werden könnten, nicht sind. Dies bedeutet nicht, dass es ein Speicherleck gibt. Es bedeutet, dass der Speicher nicht benötigt wird.

BTW: Sie sollten Thread nicht direkt erweitern. Stattdessen schlage ich vor, dass Sie einen Executors.newCachedThreadPool() verwenden. Dies wird kurzlebige Threads viel effizienter behandeln.

Auch wenn ServerSocket eine Ausnahme auslöst, kann dies nicht wiederhergestellt werden. Es bedeutet, dass wenn es einmal fehlschlägt, es endlos scheitern wird. Wenn eine Ausnahme ausgelöst wird, ist es im Allgemeinen eine schlechte Idee, sie auszudrucken, als ob sie nicht passiert wäre.

Eine kürzere Version Ihres Hauptes, die nicht endlos fehl ist

public static void main(String... args) throws IOException { 
    ServerSocket serverSock = new ServerSocket(9999); 
    ExecutorService es = Executors.newCachedThreadPool(); 
    while (true) 
     es.submit(new ClientHandler(serverSock.accept())); 
    } 
} 
class ClientHandler implements Runnable { 
    private final Socket socket; 
    public ClientHandler(Socket socket) { this.socket = socket; } 

    public void run() { 
    while(true) { 
     // do something with the socket. 
    } 
    } 
} 
+1

Da D4N14L neu in Java ist, müssen Sie darauf hinweisen, dass, statt Thread direkt zu erweitern, er ['Runnable]] implementieren sollte (http://download.oracle.com/javase/6/docs/api/java/lang /Runnable.html) stattdessen. – Powerlord

+0

@R. Bemrose, ich wollte Callable vorschlagen, aber Runnable ist eine bessere Wahl, danke. –

+0

@R. Bemrose: Ja, ich hatte Runnable in einer früheren Version implementiert, also habe ich gerade experimentiert ... ist gerade passiert, um den Code zu posten, der den Thread erweitert hat. Trotzdem danke. – D4N14L

3

1.) Woher weißt du, dass es ein Speicherleck gibt? Es sieht nicht so aus als ob es ein Leck zu mir gibt

2.) Sie müssen nichts tun. Der Garbage Collector wird immer ausgeführt, und es ist nicht wichtig, ob die Objekte von einem Thread referenziert werden oder nicht.

3.) Das einzige was ich sehe ist, blockiert serverSock.accept() dort? Wenn nicht, werden Sie nur Threads erstellen, die so schnell sind wie die While-Schleife. Ich bin hier kein Experte, also wenn der Akzeptierungsruf dort blockiert, bevor der Thread gestartet wird, dann könnte das in Ordnung sein.

+0

'ServerSocket's [' accept'] (http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#accept%28%29) blockiert, ja. – Powerlord

2

Zwischen diesen beiden Antworten, die ich bin nicht diese Informationen zu sehen. GC bereinigt Threads, die nicht aktiv sind. Wenn Sie also den Thread die run() -Methode beenden lassen, wird er vom GC bereinigt. Jeder Speicher, der vom Stapel dieses Threads referenziert wird, wird aufgeräumt, wenn er inaktiv ist, vorausgesetzt, kein anderer aktiver Thread verweist auf den Speicher des Stacks oder auf einen statischen Root, der darauf verweist (Falmarris Antwortpunkt 2 ist falsch oder zumindest falsch formuliert). GC ist meist freihändig und automatisch, es gibt wenig Arbeit von Ihnen, um unter normalen Umständen Speicher freizugeben.

Jetzt ist der Speicher, den Sie sehen, wenn der Thread erstellt wird, anders. Wie messen Sie diesen 80K Sprung? Verwenden Sie Task-Manager, Top oder irgendein Betriebssystem-Tool, um diese Beule zu sehen? Die OS-Tools helfen nicht wirklich zu verstehen, was intern in der JVM vor sich geht. Es könnte den internen Speicher der JVM freigeben, den OS-Tools nicht sehen können. Grund ist, dass Java mit seiner Speicherzuweisung geizig ist.Sobald es mehr Speicher zuweist, behält es es bei, selbst wenn es es nicht benutzt. Grund dafür ist, dass das Betriebssystem nur langsam Speicher an Prozesse verteilt. Wenn man es festhält, wenn es später wieder benötigt wird, und die Statistik sagt ja, wird es schneller, es einfach festzuhalten, anstatt es zu befreien. Java wird den Speicher zurückgeben, wenn das Betriebssystem zu wenig Arbeitsspeicher zur Verfügung hat oder wenn nur sehr wenig Arbeitsspeicher verbraucht wird. Das ist jedoch selten der Fall. Java ist sehr effizient bei der Speicherzuweisung.

Wenn Sie Speicher sehen oder mehr Details sehen möchten, würde ich empfehlen, jconsole zu verwenden, die mit der JVM geliefert wird. Es zeigt Ihnen die Interna der JVM, so dass Sie den Gesamtspeicher, den Heap und 4 Leerzeichen (Eden, Survivor, Tenured, Perm Generation) sehen können, die den Heap bilden. Sie können auch sehen, wie viel Speicher von Ihrem Code im Code Gen Space belegt ist. Sie können sogar sehen, dass Ihre aktiven Threads im Prozess ausgeführt werden. Wenn Sie also Threads hängen haben, können Sie dies auch dort diagnostizieren. Und Sie haben die Möglichkeit, GC anzufordern, damit Sie sehen können, wie Ihre App Speicher verwendet.

Der springende Punkt ist, wenn Sie denken, Sie haben ein Leck, können Sie anfangen, Sinn zu machen, indem Sie dieses Werkzeug verwenden. Es ist super einfach, wirklich informativ und kostenlos. Wenn Sie ein Leck haben, benötigen Sie möglicherweise einen Speicherprofiler wie jprofiler, um mehr Details zu erfahren.

+0

Danke, das war für einen Neuling wie mich sehr informativ. Ja, ich habe den Task-Manager verwendet ... Ich werde jConsole für spätere Probleme berücksichtigen. – D4N14L

Verwandte Themen