Obtener información del sistema a nivel del sistema operativo

232

Actualmente estoy construyendo una aplicación Java que podría terminar ejecutándose en muchas plataformas diferentes, pero principalmente variantes de Solaris, Linux y Windows.

¿Alguien ha podido extraer con éxito información como el espacio de disco actual utilizado, la utilización de la CPU y la memoria utilizada en el sistema operativo subyacente? ¿Qué pasa con lo que está consumiendo la aplicación Java?

Preferiblemente me gustaría obtener esta información sin usar JNI.

Steve M
fuente
3
Con respecto a la memoria libre, consulte stackoverflow.com/a/18366283/231397 ( Runtime.getRuntime().freeMemory()como se sugiere en la respuesta aceptada NO le proporciona la cantidad de memoria libre.
Christian Fries

Respuestas:

206

Puede obtener información de memoria limitada de la clase Runtime. Realmente no es exactamente lo que está buscando, pero pensé que se lo proporcionaría por completo. Aquí hay un pequeño ejemplo. Editar: También puede obtener información sobre el uso del disco de la clase java.io.File. El uso del espacio en disco requiere Java 1.6 o superior.

public class Main {
  public static void main(String[] args) {
    /* Total number of processors or cores available to the JVM */
    System.out.println("Available processors (cores): " + 
        Runtime.getRuntime().availableProcessors());

    /* Total amount of free memory available to the JVM */
    System.out.println("Free memory (bytes): " + 
        Runtime.getRuntime().freeMemory());

    /* This will return Long.MAX_VALUE if there is no preset limit */
    long maxMemory = Runtime.getRuntime().maxMemory();
    /* Maximum amount of memory the JVM will attempt to use */
    System.out.println("Maximum memory (bytes): " + 
        (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));

    /* Total memory currently available to the JVM */
    System.out.println("Total memory available to JVM (bytes): " + 
        Runtime.getRuntime().totalMemory());

    /* Get a list of all filesystem roots on this system */
    File[] roots = File.listRoots();

    /* For each filesystem root, print some info */
    for (File root : roots) {
      System.out.println("File system root: " + root.getAbsolutePath());
      System.out.println("Total space (bytes): " + root.getTotalSpace());
      System.out.println("Free space (bytes): " + root.getFreeSpace());
      System.out.println("Usable space (bytes): " + root.getUsableSpace());
    }
  }
}
William Brendel
fuente
77
Creo que "Memoria total actualmente en uso por la JVM" es un poco confuso. El javadoc dice que la función devuelve "la cantidad total de memoria disponible actualmente para objetos actuales y futuros, medida en bytes". Suena más como memoria restante y no en uso.
Dirk
@Dirk: Actualicé la redacción para abordar su comentario. ¡Gracias!
William Brendel
@LeonardoGaldioli: No conozco las características de rendimiento de estas clases y métodos, pero no me sorprendería si no estuvieran optimizados para la velocidad. Otras respuestas explican cómo recopilar cierta información utilizando JMX, que podría ser más rápido.
William Brendel
15
Esto no responde la pregunta correctamente. Todos esos datos se transfieren a JVM y no al sistema operativo ...
Alvaro
Sé que este tema está bastante desactualizado. PERO: si realmente necesita la cantidad de núcleos de CPU, entonces no use esta solución. Tengo una CPU de doble núcleo con dos hilos para cada núcleo. La JVM no devuelve los núcleos de hardware, sino los núcleos / subprocesos de software.
F_Schmidt
95

El paquete java.lang.management le brinda mucha más información que Runtime; por ejemplo, le proporcionará memoria heap ( ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()) separada de la memoria no heap (ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage() ).

También puede obtener el uso de CPU de proceso (sin escribir su propio código JNI), pero debe convertirlo java.lang.management.OperatingSystemMXBeanen uncom.sun.management.OperatingSystemMXBean . Esto funciona en Windows y Linux, no lo he probado en otros lugares.

Por ejemplo ... llame al método get getCpuUsage () con más frecuencia para obtener lecturas más precisas.

public class PerformanceMonitor { 
    private int  availableProcessors = getOperatingSystemMXBean().getAvailableProcessors();
    private long lastSystemTime      = 0;
    private long lastProcessCpuTime  = 0;

    public synchronized double getCpuUsage()
    {
        if ( lastSystemTime == 0 )
        {
            baselineCounters();
            return;
        }

        long systemTime     = System.nanoTime();
        long processCpuTime = 0;

        if ( getOperatingSystemMXBean() instanceof OperatingSystemMXBean )
        {
            processCpuTime = ( (OperatingSystemMXBean) getOperatingSystemMXBean() ).getProcessCpuTime();
        }

        double cpuUsage = (double) ( processCpuTime - lastProcessCpuTime ) / ( systemTime - lastSystemTime );

        lastSystemTime     = systemTime;
        lastProcessCpuTime = processCpuTime;

        return cpuUsage / availableProcessors;
    }

    private void baselineCounters()
    {
        lastSystemTime = System.nanoTime();

        if ( getOperatingSystemMXBean() instanceof OperatingSystemMXBean )
        {
            lastProcessCpuTime = ( (OperatingSystemMXBean) getOperatingSystemMXBean() ).getProcessCpuTime();
        }
    }
}
Patrick Wilkes
fuente
10
Para que se compile, reemplazar el elenco OperatingSystemMXBeande com.sun.management.OperatingSystemMXBeany prefacio de todas las instancias de getOperatingSystemMXBean()con ManagementFactory.. Necesita importar todas las clases de manera adecuada.
tmarthal
2
Estoy obteniendo el uso de la CPU como 0 para todo. Cambié la fórmula de uso de la CPU a cpuUsage = processCpuTime / systemTime. Estoy obteniendo un valor para el uso de la CPU que no entiendo.
Raj
¿significa 1.0 como resultado de getCpuUsageque el sistema está utilizando todos sus Procesadores disponibles al 100%?
user454322
2
Siempre obtengo 0 como dijo @Raj ... ¿podría dar un ejemplo de cómo usar ese código?
dm76
1
Prueba((double)( processCpuTime - lastProcessCpuTime )) / ((double)( systemTime - lastSystemTime ))
Anthony O.
43

Creo que el mejor método es implementar la API SIGAR de Hyperic . Funciona para la mayoría de los principales sistemas operativos (casi cualquier cosa moderna) y es muy fácil trabajar con él. Los desarrolladores son muy receptivos en sus foros y listas de correo. También me gusta que tenga licencia GPL2 Apache . ¡También proporcionan un montón de ejemplos en Java!

SIGAR == Información del sistema, herramienta de recopilación e informes.

Matt Cummings
fuente
2
@Yohan - ¡No seas perezoso! Puede averiguarlo leyendo la página web vinculada. (Y depende de lo que quiera decir con "plataforma independiente".)
Stephen C
2
@StephenC: Sigar está utilizando archivos .dll, lo que lo hace dependiente de la plataforma. El API de nivel superior podría estar en Java, es una historia diferente
Lemon Juice
1
@Artificial_Intelligence sí, pero proporciona bibliotecas (escritas en c) para las plataformas más populares. No depende más de la plataforma que el propio jvm. La API Java de nivel superior debe ser coherente en todas las plataformas.
Jeshurun
8
Sigar no se actualiza desde 2010 y parece tener un error en los sistemas de 64 bits: stackoverflow.com/questions/23405832/…
Alvaro
1
Y SIGAR también bloquea el JVM (aunque sea intermitente), pero estoy seguro de que no correrá este riesgo en la producción.
AKS
25

Hay un proyecto de Java que usa JNA (por lo que no hay que instalar bibliotecas nativas) y está en desarrollo activo. Actualmente es compatible con Linux, OSX, Windows, Solaris y FreeBSD y proporciona información de RAM, CPU, batería y sistema de archivos.

dB.
fuente
Ninguna biblioteca nativa es quizás engañosa. El proyecto utiliza bibliotecas nativas, incluso si no han escrito ninguna, y aparentemente no necesita instalar ninguna.
Stephen C
1
Tienes razón. JNA usa libffi que tiene componentes nativos. Pero a todos los efectos, parece que no hay bibliotecas nativas (definitivamente ninguna para instalar).
dB.
@StephenC aunque lo que dices es exacto, es engañoso porque es lo mismo para rt.jar que también invoca métodos nativos. La única razón por la que las personas se preocupan por los métodos nativos es porque tienen que compilarlos o instalarlos, lo que a menudo es una tarea no trivial. Dado que libffi es tan ampliamente adoptado, portado e instalado, mitiga las dificultades. entonces, técnicamente tienes razón, pero prácticamente no importa.
rbp
@rbp: hay otra razón por la cual las personas por las que los desarrolladores experimentados de Java prefieren evitar las bibliotecas nativas. Una biblioteca nativa que tiene errores (incluidos problemas de seguridad de subprocesos o problemas con la administración de memoria) puede desestabilizar la JVM del host. Este no es un problema de "no importa" ...
Stephen C
@StephenC ¿el haber creado el servidor de aplicaciones weblogic me ha hecho tener suficiente experiencia ?
rbp
13

Para windows fui por aquí.

    com.sun.management.OperatingSystemMXBean os = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();

    long physicalMemorySize = os.getTotalPhysicalMemorySize();
    long freePhysicalMemory = os.getFreePhysicalMemorySize();
    long freeSwapSize = os.getFreeSwapSpaceSize();
    long commitedVirtualMemorySize = os.getCommittedVirtualMemorySize();

Aquí está el enlace con detalles.

Yan Khonski
fuente
11

Puede obtener información a nivel del sistema utilizando System.getenv(), pasando el nombre de la variable de entorno relevante como parámetro. Por ejemplo, en Windows:

System.getenv("PROCESSOR_IDENTIFIER")
System.getenv("PROCESSOR_ARCHITECTURE")
System.getenv("PROCESSOR_ARCHITEW6432")
System.getenv("NUMBER_OF_PROCESSORS")

Para otros sistemas operativos, la presencia / ausencia y los nombres de las variables de entorno relevantes serán diferentes.

Alexandr
fuente
1
Estos dependen de la plataforma porque los nombres de las variables son diferentes entre los sistemas. El artículo de Oracle sobre variables de entorno dice eso. También estoy encontrando una manera de obtener un sistema independiente.
Lemon Juice
Bajo Linux (Ubuntu 17.10) no hay mucha información interesante sobre los procesadores en el entorno.
pveentjer
8

Agregue dependencia OSHI a través de maven:

<dependency>
    <groupId>com.github.dblock</groupId>
    <artifactId>oshi-core</artifactId>
    <version>2.2</version>
</dependency>

Obtenga una capacidad de batería restante en porcentaje:

SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
for (PowerSource pSource : hal.getPowerSources()) {
    System.out.println(String.format("%n %s @ %.1f%%", pSource.getName(), pSource.getRemainingCapacity() * 100d));
}
Oleksii Kyslytsyn
fuente
OSHI tiene la mayor parte de la información descrita en los otros comentarios. Utiliza JNA para obtenerlo a través de llamadas nativas del sistema operativo cuando sea posible.
Daniel Widdis
6

Eche un vistazo a las API disponibles en el paquete java.lang.management . Por ejemplo:

  • OperatingSystemMXBean.getSystemLoadAverage()
  • ThreadMXBean.getCurrentThreadCpuTime()
  • ThreadMXBean.getCurrentThreadUserTime()

Hay muchas otras cosas útiles allí también.

staffan
fuente
2
OperatingSystemMXBean.getSystemLoadAverage () no está implementada en Windows, ya que "es demasiado caro"
MikeNereson
1
ThreadMXBean.getCurrentThreadCpuTime () solo devuelve cuánto tiempo se ha estado ejecutando ese subproceso. No es el porcentaje de uso de la CPU.
MikeNereson
5

Por lo general, para obtener información del sistema operativo de bajo nivel, puede llamar a comandos específicos del sistema operativo que le brindan la información que desea con Runtime.exec () o leer archivos como / proc / * en Linux.

Peter Lawrey
fuente
5

El uso de la CPU no es sencillo: java.lang.management a través de com.sun.management.OperatingSystemMXBean.getProcessCpuTime se acerca (consulte el excelente fragmento de código de Patrick arriba) pero tenga en cuenta que solo da acceso al tiempo que la CPU pasó en su proceso. no le informará sobre el tiempo de CPU empleado en otros procesos, o incluso el tiempo de CPU dedicado a realizar actividades del sistema relacionadas con su proceso.

Por ejemplo, tengo un proceso Java intensivo en red: es lo único que se ejecuta y la CPU está al 99%, pero solo el 55% de eso se informa como "CPU del procesador".

ni siquiera me hagas comenzar con el "promedio de carga", ya que es casi inútil, a pesar de ser el único elemento relacionado con la CPU en el bean MX. si solo el sol en su sabiduría ocasional expusiera algo como "getTotalCpuTime" ...

para un monitoreo serio de la CPU, SIGAR mencionado por Matt parece ser la mejor apuesta.

Parcialmente nublado
fuente
4

Encendido Windows, puede ejecutar el systeminfocomando y recuperar su salida, por ejemplo, con el siguiente código:

private static class WindowsSystemInformation
{
    static String get() throws IOException
    {
        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec("systeminfo");
        BufferedReader systemInformationReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

        StringBuilder stringBuilder = new StringBuilder();
        String line;

        while ((line = systemInformationReader.readLine()) != null)
        {
            stringBuilder.append(line);
            stringBuilder.append(System.lineSeparator());
        }

        return stringBuilder.toString().trim();
    }
}
BullyWiiPlaza
fuente
3

Si está utilizando Jrockit VM, aquí hay otra forma de obtener el uso de CPU de VM. El bean de tiempo de ejecución también puede proporcionarle una carga de CPU por procesador. He usado esto solo en Red Hat Linux para observar el rendimiento de Tomcat. Debe habilitar el control remoto JMX en catalina.sh para que esto funcione.

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://my.tomcat.host:8080/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);     
MBeanServerConnection conn = jmxc.getMBeanServerConnection();       
ObjectName name = new ObjectName("oracle.jrockit.management:type=Runtime");
Double jvmCpuLoad =(Double)conn.getAttribute(name, "VMGeneratedCPULoad");
petro
fuente
3

Todavía está en desarrollo pero ya puedes usar jHardware

Es una biblioteca simple que elimina los datos del sistema utilizando Java. Funciona tanto en Linux como en Windows.

ProcessorInfo info = HardwareInfo.getProcessorInfo();
//Get named info
System.out.println("Cache size: " + info.getCacheSize());        
System.out.println("Family: " + info.getFamily());
System.out.println("Speed (Mhz): " + info.getMhz());
//[...]
profesor_falken
fuente
Agradable, pero utiliza versiones de Guava y JNA que están en conflicto con mis necesidades (por ejemplo, ver GLASSFISH-21367 ).
lu_ko
Hola, JNA se ha introducido en la versión 0.8 de jHardware. Solo se usa para datos de temperatura y sensores. Si no necesita esa información, puede usar la versión 0.7. Lo mismo para la guayaba. En ese caso, tendrá que usar la versión 0.6.3.
profesor_falken
2

Una forma sencilla que se puede utilizar para obtener la información a nivel del sistema operativo y probé en mi Mac que funciona bien:

 OperatingSystemMXBean osBean =
        (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
    return osBean.getProcessCpuLoad();

Puede encontrar muchas métricas relevantes del sistema operativo aquí

Amandeep Singh
fuente
1

Hola, puedes hacer esto con la integración java / com. Al acceder a las funciones de WMI puede obtener toda la información.


fuente
0

Para obtener el promedio de carga del sistema de 1 minuto, 5 minutos y 15 minutos dentro del código de Java, puede hacerlo ejecutando el comando cat /proc/loadavgusando e interpretándolo de la siguiente manera:

    Runtime runtime = Runtime.getRuntime();

    BufferedReader br = new BufferedReader(
        new InputStreamReader(runtime.exec("cat /proc/loadavg").getInputStream()));

    String avgLine = br.readLine();
    System.out.println(avgLine);
    List<String> avgLineList = Arrays.asList(avgLine.split("\\s+"));
    System.out.println(avgLineList);
    System.out.println("Average load 1 minute : " + avgLineList.get(0));
    System.out.println("Average load 5 minutes : " + avgLineList.get(1));
    System.out.println("Average load 15 minutes : " + avgLineList.get(2));

Y para obtener la memoria del sistema físico ejecutando el comando free -my luego interpretándolo de la siguiente manera:

Runtime runtime = Runtime.getRuntime();

BufferedReader br = new BufferedReader(
    new InputStreamReader(runtime.exec("free -m").getInputStream()));

String line;
String memLine = "";
int index = 0;
while ((line = br.readLine()) != null) {
  if (index == 1) {
    memLine = line;
  }
  index++;
}
//                  total        used        free      shared  buff/cache   available
//    Mem:          15933        3153        9683         310        3097       12148
//    Swap:          3814           0        3814

List<String> memInfoList = Arrays.asList(memLine.split("\\s+"));
int totalSystemMemory = Integer.parseInt(memInfoList.get(1));
int totalSystemUsedMemory = Integer.parseInt(memInfoList.get(2));
int totalSystemFreeMemory = Integer.parseInt(memInfoList.get(3));

System.out.println("Total system memory in mb: " + totalSystemMemory);
System.out.println("Total system used memory in mb: " + totalSystemUsedMemory);
System.out.println("Total system free memory in mb: "   + totalSystemFreeMemory);
Krishna Prasad
fuente