1

入力ストリーム、特にreadObjectから読み取るこのスレッドで奇妙な問題が発生しています。ログデバッグステートメントを配置しようとしたところ、スレッドがブロックを示しているため、スレッドは想定どおりに呼び出しをブロックしています。問題は、このスレッドがまだプロファイラーで実行中としてマークされており、CPU使用率の50%を占めていることです。私はこれと同様のスレッドを持っており、適切にブロックされ、ブロックされると0%のCPUを使用します。私はここで何がうまくいかないのか困惑しています。

私は新規ユーザーですので画像を投稿できませんので、ご覧ください。画像の緑色は実行中、黄色はブロックまたは待機中を意味します。

http://i.imgur.com/5FPyZ.pngここで 拡大縮小されていない画像も利用できます:

主要

  {
    SocketFactory factory = SocketFactory.getDefault();
    Socket tcpSocket = factory.createSocket("localhost", 5011);
    IoTcpReadRunnable ioTcpReadRunnable = new IoTcpReadRunnable(new MessageProcessor()
    {
        @Override
        public void enqueueReceivedMessage(Object message)
        {
            System.out.println("MessageReceived Enqueued.");
        }

        @Override
        public void enqueueMessageToWrite(Envelope message)
        {
            System.out.println("Message Enqueued to Write.");
        }
    }, tcpSocket);

    new Thread(ioTcpReadRunnable, "ClientExample IoTcpRead").start(); 

}

TcpRead Runnable

public final class IoTcpReadRunnable implements Runnable {
public static final Logger logger = LoggerFactory.getLogger(IoTcpReadRunnable.class);
protected MessageProcessor<MessageType> messageProcessor = null;
protected Socket tcpSocket = null;
protected ObjectOutputStream outputStream = null;
protected ObjectInputStream inputStream = null;
protected boolean connected = false;

public IoTcpReadRunnable(MessageProcessor<MessageType> messageProcessor, Socket tcpSocket)
{
    this.messageProcessor = messageProcessor;
    this.tcpSocket = tcpSocket;
    this.init();
}

protected void init()
{
    try
    {
        this.outputStream = new ObjectOutputStream(tcpSocket.getOutputStream());
        this.outputStream.flush();

        this.inputStream = new ObjectInputStream(tcpSocket.getInputStream());
    }
    catch (IOException ex)
    {
        logger.error("Tcp Socket Init Error Error ", ex);
    }

}

public boolean isConnected()
{
    return connected;
}

protected synchronized Object readObject() throws IOException, ClassNotFoundException
{
    Object readObject = null;
    //blocks here
    logger.trace("{} About to block for read Object");

    readObject = this.inputStream.readObject();
    logger.trace("{} Read Object from Stream: {} ", "", readObject);

    return readObject;
}

public void close()
{
    try
    {
        //todo
        this.connected = false;
        this.outputStream.flush();
        this.outputStream.close();
        this.inputStream.close();
        synchronized (tcpSocket)
        {
            this.tcpSocket.close();
        }
    }
    catch (IOException ex)
    {
        logger.error("Error closing Socket");
    }
}

@Override
public void run()
{

    this.connected = true;

    while (this.connected)
    {
        try
        {
            Object readObject = readObject();

            if (readObject != null)
            {
                this.messageProcessor.enqueueReceivedMessage((MessageType) readObject);
            }
            else
            {
                logger.error("Read Object is null");
            }
        }
        catch (IOException ex)
        {
            logger.error("TcpRecieveThread IOException", ex);
        }
        catch (ClassNotFoundException ex)
        {
            logger.error("TcpRecieveThread ClassnotFound", ex);
        }
    }
    this.close();

}
}
4

2 に答える 2

2
  1. プロファイルします。それはあなたに多くを教えてくれるでしょう。

  2. はい-JavaObjectIOStreamsを使用したオブジェクトのシリアル化と逆シリアル化は、比較的CPUに負荷がかかります。

  3. IOパイプラインでBufferedInputStream/BufferedOutputStreamを使用していないように見えるため、データが一度に1バイトずつ読み書きされている可能性があります。これにより、CPU使用率が大幅に増加する可能性があります。


このスレッドはブロックされ、ReadObjectが呼び出されたときにCPUの0%を占める必要があると言っています。

I / Oが一般的にreadObject、特にどのように機能するかを根本的に誤解していると思います。メソッドを呼び出すと、ソケットからバイトをフェッチするために1つ(または場合によっては多数)のシステムコールが実行されます。次に、それらのバイトをデコードして、作成する必要のあるオブジェクトを特定し、それらを作成して初期化します。この作業はすべて、「ユーザー時間」または「システム時間」として処理されるCPU時間を要します。CPUがプロセスに考慮されていないのは、readシステムコールがネットワークパケットの到着を待機する必要がある場合のみです。

また、これらの「プロファイリング」の結果も信じられません。まず、コードを簡単に読むと、mainメソッドが新しいスレッドを作成し、それを開始してすぐに戻ることがわかります。しかし、これらの結果は、「メイン」スレッドが継続的に実行されていることを示しているようです。それは無意味です...そして、プロファイリングを行うために使用している方法論および/またはそれらを私たちに解釈している方法に疑問を投げかけます。


もう1つ確認することがあります。ログファイルを見ましたか?ロギングは正しく構成されていますか?

于 2012-06-07T03:36:12.180 に答える
1

それを改善するために、このように見ることができるかもしれません。

  1. カスタムの writeObject と readObject() を使用してみてください

  2. Google プロトコル バッファを試す

  3. ストリームの圧縮

  4. オブジェクトをシリアル化するときは、必要な属性に対してのみシリアル化を提供します。オブジェクト全体をシリアル化しないでください。したがって、transient を使用してください。

この議論を見る価値があります: Java Object Serialization Performance tips

于 2012-06-07T04:25:59.027 に答える