Los sistemas operativos realizan operaciones de E / S en áreas de memoria. Estas áreas de memoria, en lo que respecta al sistema operativo, son secuencias contiguas de bytes. No sorprende entonces que solo los buffers de bytes sean elegibles para participar en las operaciones de E / S. Recuerde también que el sistema operativo accederá directamente al espacio de direcciones del proceso, en este caso el proceso JVM, para transferir los datos. Esto significa que las áreas de memoria que son objetivos de las operaciones de E / S deben ser secuencias contiguas de bytes. En la JVM, una matriz de bytes no puede almacenarse contiguamente en la memoria, o el recolector de basura podría moverla en cualquier momento. Las matrices son objetos en Java, y la forma en que se almacenan los datos dentro de ese objeto podría variar de una implementación de JVM a otra.
Por esta razón, se introdujo la noción de un buffer directo. Los buffers directos están destinados a la interacción con canales y rutinas de E / S nativas. Hacen un gran esfuerzo para almacenar los elementos de bytes en un área de memoria que un canal puede usar para acceso directo o sin formato mediante el uso de código nativo para indicar al sistema operativo que drene o llene el área de memoria directamente.
Los buffers de byte directo suelen ser la mejor opción para las operaciones de E / S. Por diseño, admiten el mecanismo de E / S más eficiente disponible para la JVM. Los búferes de bytes no directos se pueden pasar a los canales, pero al hacerlo puede incurrir en una penalización de rendimiento. Por lo general, no es posible que un búfer no directo sea el objetivo de una operación de E / S nativa. Si pasa un objeto ByteBuffer no directo a un canal para escritura, el canal puede hacer lo siguiente implícitamente en cada llamada:
- Cree un objeto ByteBuffer directo temporal.
- Copie el contenido del búfer no directo al búfer temporal.
- Realice la operación de E / S de bajo nivel utilizando el búfer temporal.
- El objeto de búfer temporal queda fuera de alcance y finalmente se recolecta basura.
Potencialmente, esto puede dar lugar a la copia del búfer y la rotación de objetos en cada E / S, que son exactamente el tipo de cosas que nos gustaría evitar. Sin embargo, dependiendo de la implementación, las cosas pueden no ser tan malas. El tiempo de ejecución probablemente almacenará en caché y reutilizará memorias intermedias directas o realizará otros trucos inteligentes para aumentar el rendimiento. Si simplemente está creando un búfer para un solo uso, la diferencia no es significativa. Por otro lado, si va a usar el búfer repetidamente en un escenario de alto rendimiento, es mejor que asigne los búferes directos y los reutilice.
Las memorias intermedias directas son óptimas para E / S, pero pueden ser más caras de crear que las memorias intermedias de bytes no directas. La memoria utilizada por las memorias intermedias directas se asigna llamando al código nativo específico del sistema operativo, sin pasar por el montón JVM estándar. La configuración y el desmantelamiento de los buffers directos podrían ser significativamente más caros que los buffers residentes en el montón, dependiendo del sistema operativo host y la implementación de JVM. Las áreas de almacenamiento de memoria de los buffers directos no están sujetas a recolección de basura porque están fuera del montón JVM estándar.
Las compensaciones de rendimiento del uso de buffers directos versus no directos pueden variar ampliamente según la JVM, el sistema operativo y el diseño del código. Al asignar memoria fuera del montón, puede someter su aplicación a fuerzas adicionales que JVM desconoce. Cuando ponga en juego partes móviles adicionales, asegúrese de lograr el efecto deseado. Recomiendo la vieja máxima del software: primero haz que funcione, luego hazlo rápido. No se preocupe demasiado por la optimización por adelantado; concentrarse primero en lo correcto. La implementación de JVM puede realizar el almacenamiento en caché del búfer u otras optimizaciones que le brindarán el rendimiento que necesita sin un esfuerzo innecesario de su parte.