java - móviles - manual de programacion android pdf
¿Por qué select() consume tanto tiempo de CPU en mi programa? (4)
Tengo varias aplicaciones Java que usan MINA, y todas usan 20 hilos MINA. Una aplicación sirve alrededor de 10,000 conexiones concurrentes, que usualmente están inactivas pero a veces reciben entrada. Es probable que 20 sea una cuenta de hilos razonable para esa aplicación, aunque no la he perfilado exactamente (a lo que se refiere esta pregunta). Otra aplicación sirve solo alrededor de 15 conexiones a la vez, pero inicia el trabajo de IO por lo que está muy ocupada y tiene 20 hilos MINA de todos modos, lo que obviamente es demasiado.
Lo que me extraña es que ambas aplicaciones siempre dedican aproximadamente el 30%, a veces tan alto como el 60%, de su tiempo de CPU al método select () de MINA, perfilado en VisualVM. La pila de llamadas se ve así:
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:228)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:81)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
- locked <40ca5d54> (a sun.nio.ch.Util$2)
- locked <24649fe8> (a java.util.Collections$UnmodifiableSet)
- locked <3fae9662> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98)
at org.apache.mina.transport.socket.nio.NioProcessor.select(NioProcessor.java:72)
at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:1093)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Parece estar basado en una encuesta ocupada, lo que me suena realmente mal.
¿Debo estar preocupado cuando veo el número tan alto? ¿Qué causa esto? ¿Es algo que necesito para optimizar o es más parecido a una rutina de inactividad o inactividad? Si se parece más a una rutina de suspensión, ¿está programada de alguna manera para tener una prioridad más baja que otras tareas de la CPU?
Actualización: este hilo parece ser el mismo problema. Seguí su consejo, y ahora estoy ejecutando Java 1.7.0_45, pero aún veo una select
tan solo el 90% del tiempo de CPU en una aplicación con 10k conexiones.
Estamos utilizando MINA 2.0.4, lo que significa que este error relevante está arreglado.
Al igual que la respuesta de la pregunta vinculada a la que se hace referencia, el problema común son los errores JDK anteriores. Dado que ahora está en una versión actualizada, creo que esto podría ser realmente un cuello de botella de hardware.
Aquí hay un enlace a un problema de glassfish que describe dónde se discute la posibilidad de que el hardware (red y servidores) sea la fuente del problema.
Además, aquí hay otra pregunta similar sin respuesta todavía: SelectorImpl está BLOQUEADO
Desafortunadamente, esta es una interpretación errónea de los números.
He enfrentado esta situación muchas veces (y también hago una pregunta sobre ).
La razón principal, es que VisualVM no muestra el tiempo de CPU correcto. Se muestra el porcentaje del tiempo de subproceso en estado RUNNING
. Pero a partir de la documentación en Thread.State
:
Estado del hilo para un hilo ejecutable. Un subproceso en el estado ejecutable se está ejecutando en la máquina virtual Java, pero puede estar esperando otros recursos del sistema operativo , como el procesador.
Esto es exactamente lo que está pasando. En realidad, el hilo está bloqueado dentro de la llamada epoll_wait()
sistema epoll_wait()
. En el cuadro de Linux hay varias formas de confirmar que es así.
hilo de rosca
$ strace -tttT -f -p [thread-id]
El id del hilo se puede obtener de la salida de jstack
:
$ jstack [java-pid]
[...]
"Netty Builtin Server 1" #17 prio=5 os_prio=31 tid=0x00000001013dd800 nid=0xe12f runnable [0x0000700001fe4000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method)
at sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:198)
[...]
en este caso, el ID de hilo es 0xe12f
(se debe convertir a decimal). Verás que la mayor parte del tiempo estará en la llamada epoll_wait()
.
pidstat
hilo
$ pidstat -tu -p [java-pid] | grep [thread pid]
Verá un sistema bajo así como el tiempo de CPU del usuario por este hilo, lo que significa que no consume CPU.
estado del hilo de sondeo usando ps
$ ps -eL -o pid,tid,state | grep [thread-id]
Verás la mayor parte del tiempo en el estado S
o Sl
(sueño interrumpible) en lugar de R
(ejecutable).
Al final, no debe preocuparse por eso si no hay problemas operativos con el servicio.
En primer lugar, es bueno que ambas aplicaciones tengan el mismo problema; probablemente indica que el problema es con la JVM o el sistema operativo y no con su aplicación :-)
Como mencionó jzd, nio.select()
ha tenido problemas. La multiplicación de {varias versiones de Java} x {varias plataformas, versiones del kernel} hace que parezca un problema general. Espero que una de estas obras para ti:
Si está en Linux, pruebe el kernel
2.6
en caso de que esté en2.4
, asumiendo que el error es similar a: http://bugs.sun.com/view_bug.do?bug_id=6670302
Utilice una versión anterior de JRE / JDK, ¡no la última versión!
, es decir, volver a JRE 6 / JDK 6 en lugar de 7.
Tratar
- {versión anterior de JRE (6), versión anterior de kernel de Linux} o
- {versión más reciente de JRE (7), versión más reciente del kernel de Linux}
en lugar de mezclarlos como en {anterior, más nuevo} o {más nuevo, más antiguo}.
Una aplicación está sondeando 10.000 conexiones, utilizando muy poca CPU por conexión, pero en conjunto podría sumar una buena fracción del tiempo de CPU. Toda prioridad es permitir que algún otro trabajo se ponga en línea primero.
La otra aplicación que tiene menos conexiones pero que hace más problemas por conexión también podría mostrar el porcentaje más alto, pero debería mostrar una fracción menor del tiempo de espera y una fracción mayor de CPU.