Monitorando a JVM – Memória Utilizada

Existe uma ferramenta da própria JDK que nos auxilia no monitoramento da JVM.


  1. Acesse a pasta JDK do seu PC,
  2. A pasta BIN,
  3. Execute “JConsole”

Esta ferramenta lhe permitirá o monitoramento.



Artigo (retirado do site da Oracle em http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html)

Usando JConsole para monitorar aplicativos

Por Chung Mandy, Dezembro de 2004

O Java 2 Platform, Standard Edition (J2SE) 5.0 versão oferece acompanhamento abrangente e apoio à gestão. Ele não apenas define as interfaces de gestão para a máquina virtual Java, mas também fornece out-of-the-box-monitoramento remoto e gerenciamento na plataforma Java e de aplicativos que rodam nele. Além disso, o JDK 5.0 inclui o acompanhamento e Java Management Console (JConsoleferramenta). Ele usa a instrumentação extensiva da máquina virtual Java para fornecer informações sobre o desempenho eo consumo de recursos de aplicações em execução na plataforma Java utilizando o Java Management Extension (JMX) tecnologia.

O artigo descreve como JConsole pode ser usado para observar informações sobre um aplicativo rodando na plataforma Java. O primeiro artigo apresenta uma visão geral do acompanhamento do J2SE 5.0 e arquitetura de gerenciamento e como plugs JConsole na arquitetura. Em seguida, ele descreve como usar JConsole para monitoramento de acesso e core várias funcionalidades sob a gestão da plataforma Java, incluindo:

Visão geral sobre arquitetura

A Figura 1 mostra a arquitetura de monitoramento J2SE 5.0 e suporte à gestão. Na versão 5.0, a máquina virtual Java (JVM) é altamente instrumentados para monitoramento e gerenciamento. A instrumentação da plataforma fornece informações sobre o desempenho, consumo de recursos, ea JVM e configurações de log de aplicativos em execução na plataforma Java.

JMX oferece uma maneira padrão de instrumento, o Java Runtime Environment e aplicações, e JMX Remote API permite que a instrumentação a ser acessado remotamente. A instrumentação é acessível através do JMX bean gerenciado (MBean) interfaces, que são registrados na plataforma do servidor MBean. Os aplicativos também podem criar seus próprios MBeans e registrá-los na plataforma do servidor MBean, que pode servir como um ponto único para acesso remoto. Um cliente JMX-compliant, como JConsole, pode se conectar ao servidor MBean plataforma e gerir a aplicação (assim como a plataforma Java), usando a tecnologia JMX.

Figure 1: Architecture of J2SE 5.0 Monitoring and Management Support

Figura 1: Arquitetura de 5,0 J2SE Monitoramento e Apoio à Gestão.

Plataforma de Feijão

A plataforma Java fornece um conjunto de MBeans plataforma (Managed Beans) para a monitorização e gestão da máquina virtual Java eo recurso de log:

Plataforma MBean Descrição
java.lang.management.ClassLoadingMXBean Classe de carregamento do sistema da máquina virtual Java.
java.lang.management.CompilationMXBean Compilação do sistema da máquina virtual Java.
java.lang.management.MemoryMXBean sistema de memória da máquina virtual Java.
java.lang.management.MemoryManagerMXBean O gerenciador de memória na máquina virtual Java.
java.lang.management.MemoryPoolMXBean pool de memória da máquina virtual Java.
java.lang.management.GarbageCollectorMXBean Coletor de lixo na máquina virtual Java.
java.lang.management.ThreadMXBean Segmentação do sistema da máquina virtual Java.
java.lang.management.RuntimeMXBean Tempo de execução do sistema da máquina virtual Java.
java.lang.management.OperatingSystemMXBean Sistema operacional em que a máquina virtual Java está sendo executado.
java.util.logging.LoggingMXBean Facilidade de registro.

Um MBean é um objeto gerenciado que segue os padrões de projeto em conformidade com a especificação JMX. Um MBean pode representar um dispositivo, o aplicativo, ou qualquer recurso que precisa ser gerenciado. A interface de gerenciamento de um MBean compreende um conjunto de e / ou escrita legível e atributos de um conjunto de operações invokable. MBeans também pode emitir notificações quando ocorrem eventos predefinidos.

Cada plataforma MBean possui um rico conjunto de atributos e operações, tais como uso de memória, uso de CPU discussão, as estatísticas de coleta de lixo, e assim por diante. Alguns podem também emitir notificações. Vamos explorar um MBeans plataforma poucos nas seções seguintes.

A ferramenta JConsole

JConsole é um JMX-compliant ferramenta gráfica que se conecta a uma JVM em execução, que começou com o agente de gestão. Para iniciar um aplicativo com o agente de gerenciamento para monitoramento local, defina a com.sun.management.jmxremote propriedade do sistema quando você iniciar o aplicativo. Por exemplo, para permitir a aplicação J2SE amostra Java2Demo para monitoramento local, digite o seguinte comando:

   JDK_HOME/ bin / java-jar-Dcom.sun.management.jmxremote JDK_HOME/ demo/jfc/Java2D/Java2Demo.jar

onde JDK_HOME é o diretório que contém o JDK 5.0. O Monitoramento e Gerenciamento Usando o JMX documento fornece informações detalhadas sobre fora-de-caixa o gerenciamento remoto.

Para começar JConsole, execute

   JDK_HOMEbin / jconsole

Uma caixa de diálogo de conexão é aberta, cuja Local guia lista os JVMs rodando no sistema local começou com o mesmo ID de usuário como JConsole, juntamente com seu processo de identificação e de classe / informações argumento.

Figure 2: Local Tab

Figura 2: Guia Local.

JConsole pode se conectar a uma JVM rodando em 3 maneiras diferentes:

  • Local. Conecta-se a uma máquina virtual Java rodando no sistema local usando JConsole que é executado com o mesmo nome de usuário. JConsole se conecta ao servidor MBean plataforma, usando um conector RMI com a autenticação que usa a permissão de acesso de arquivos. Esta ligação capacidade de monitoramento local só está disponível no JDK é dom
  • Remoto. Conecta-se a um agente JMX usando um conector RMI com a seguinte URL:
    Serviço: jmx: rmi: / / / jndi / rmi: / /hostName:PortNum/ jmxrmi

    onde hostName é o nome do host e PortNum é o número da porta especificado quando o agente JMX foi ativado. JConsole irá passar o nome de usuário e senha como credenciais de autenticação do cliente como para o servidor conector RMI, definindo o “jmx.remote.credentials“propriedade desses valores no mapa do ambiente para estabelecer a conexão.

  • Avançado. Conecta-se a um agente JMX com a URL especificada. Você geralmente usa esta opção para ligar a um agente JMX usando um conector personalizado diferente conector RMI, ou um aplicativo rodando no JDK 1.4 com a implementação de referência do JMX (JSR-3) e JMX Remote (JSR-160).

Quando JConsole estabelece a conexão com êxito, ele obtém informações da JVM MBeans no agente JMX ligado, e exibe as informações nas seguintes guias:

A guia MBeans exibe informações sobre todos os MBeans registrados no JVM alvo de uma forma genérica. A guia MBeans permite que você acesse o conjunto completo dos instrumentos da plataforma, incluindo aqueles que não são visíveis em outras guias. Observe que as outras abas apresentar algumas das informações sobre os MBeans plataforma. Além disso, você pode monitorar e gerenciar o seu aplicativo MBeans usando este guia MBeans.

Usando MBeans guia para monitorar e gerenciar MBeans

Todos os MBeans plataforma e aplicação que estão registrados no agente JMX conectado pode ser acessada através da aba de MBeans. Por exemplo, a memória MBean é definida como segue:

        interface pública MemoryMXBean {
            getHeapMemoryUsage MemoryUsage público ();
            getNonHeapMemoryUsage MemoryUsage público ();
            getObjectPendingFinalizationCount public int ();
            isVerbose public boolean ();
            setVerbose public void (valor booleano);
            gc public void ();
        }

A memória contém quatro atributos MBean:

  • HeapMemoryUsage. Um atributo de somente leitura que descreve o uso de memória heap corrente.
  • NonHeapMemoryUsage. Um atributo de somente leitura descrevendo heap não-uso de memória.
  • ObjectPendingFinalizationCount. Um atributo de somente leitura que descreve o número de objetos pendentes para a finalização.
  • Verbose. Um booleano atributo que descreve o rastreamento detalhado GC definição. Isso pode ser configurado dinamicamente.

A memória MBean suporta uma operação, gc, para coleta de lixo pedidos explícitos. Detalhes da interface MBean são definidos nojava.lang.management.MemoryMXBean especificação. Você pode usar a guia MBeans para acessar todos esses atributos e operações.

Figure 3: MBeans Tab

Figura 3: Aba MBeans.

A árvore da esquerda mostra a lista de todos os MBeans, organizado pelo objeto de seus nomes. Um nome de objeto MBean consiste em um nome de domínio e uma lista de propriedades da chave. Por exemplo, os MBeans plataforma para a JVM são todos agrupados sob o título “java.langdomínio “, onde o MBean registro está sob o”java.util.loggingdomínio “. O formato de um nome de objeto MBean é descrito najavax.management.ObjectName especificação.

Quando você seleciona um MBean na árvore, seus atributos, operações, notificações e outras informações são apresentadas à direita. Você pode definir o valor dos atributos, se forem graváveis (o valor é exibido em azul). Você pode invocar as operações indicadas na guia de operações. Você também pode assistir para as notificações emitidas por um MBean: por padrão, JConsole não ouvir qualquer notificação emitida por um MBean até você assiná-lo. Você pode clicar no botão “Assinar“para subscrever notificações ou”Cancelar“para cancelar.

Figure 4: MBeans Notification

Figura 4: Notificação MBeans.

Detecção de memória baixa

O separador de memória fornece informações sobre o consumo de memória, pools de memória, e as estatísticas de coleta de lixo, acessando o sistema de memória, pools de memória, MBeans coletor de lixo.

Figure 5: Memory Tab

Figura 5: Guia de memória.

O gráfico mostra o uso da memória ao longo do tempo, para a pilha e memória não-heap e pools de memória específica. Os pools de memória disponível depende da JVM está sendo usado. A lista a seguir mostra as piscinas para a máquina virtual HotSpot.

  • Eden Space (heap) Pool de que a memória é inicialmente atribuído para a maioria dos objetos.
  • Sobrevivente Space (heap) Piscina contendo objetos que sobreviveram GC de espaço Éden.
  • Docente Geração (heap) pool contendo objetos que já existem há algum tempo no espaço de sobrevivência.
  • Geração Permanente (não-heap). Armazena todos os dados refletem a máquina virtual em si, como objetos de classe e método. Com JVMs que utilizam o compartilhamento de dados de classe, esta geração está dividido em read-only e read-write áreas.
  • Código de cache (não-heap) A JVM HotSpot também inclui um “cache de código” que contém a memória usada para a compilação e armazenamento de código nativo.

Os detalhes área mostra várias métricas de memória atual:

  • Utilizado A quantidade de memória usados atualmente. Memória utilizada inclui a memória ocupada por todos os objetos incluindo objetos acessíveis e inacessíveis.
  • Comprometida A quantidade de memória garantia de estar disponível para utilização pelo JVM. A quantidade de memória comprometida podem mudar ao longo do tempo. A máquina virtual Java pode liberar memória para o sistema e comprometida pode ser menor do que a quantidade de memória originalmente alocada na inicialização. Comprometida será sempre maior ou igual a usada.
  • Capacidade A quantidade máxima de memória que pode ser utilizado para gerenciamento de memória. Seu valor pode mudar ou ser indefinido. A alocação de memória pode falhar se a JVM tenta aumentar a memória usada para ser maior do que a memória comprometida, mesmo que a quantidade utilizada é inferior ou igual ao máximo (por exemplo, quando o sistema está com pouca memória virtual).
  • Uso Limite O limite de uso de um pool de memória. Este campo só será apresentado se o pool de memória suporta limite de uso.
  • tempo GC O tempo acumulado gasto na coleta de lixo eo número total de chamadas. Pode ter várias linhas, cada uma delas representa um algoritmo de coletor de lixo usado na JVM.

O gráfico de barras na parte inferior direita mostra a memória consumida pelas piscinas de memória na JVM. O bar fica vermelho quando a memória utilizado exceder o limite de uso. O limite de utilização é um dos atributos definidos na memória pool MBean para a memória de detecção de baixo suporte. Um conjunto de métodos definidos na interface MemoryPoolMXBean para a memória de detecção de baixo suporte segue.

        interface pública MemoryPoolMXBean {
            ....
            / Limite de uso / 
            getUsageThreshold longo público ();
            setUsageThreshold public void (limite de tempo);
            public boolean isUsageThresholdExceeded ();
            public boolean isUsageThresholdSupported ();
            / / Limite de uso Collection
            getCollectionUsageThreshold longo público ();
            setCollectionUsageThreshold public void (limite de tempo);
            public boolean isCollectionUsageThresholdSupported ();
            public boolean isCollectionUsageThresholdExceeded ();
        }

Cada pool de memória pode ter dois tipos de limites de memória são utilizados para detecção de baixo suporte: um limite de uso e um limite de uso da coleção. Qualquer um destes limites, não pode ser apoiada por um pool de memória em particular.

Uso Threshold

O limite de uso é um atributo gerenciável de pools de memória. Ele permite o monitoramento do uso de memória com baixo custo operacional.Definir o limite para um valor positivo que permite verificar o limite de uso de um pool de memória. Definir o limite de uso para zero desativa a verificação de limite de uso. O valor padrão é fornecido pela JVM. A JVM executa limiar de verificação do uso de um pool de memória no momento mais apropriado, normalmente durante o GC e às vezes no momento da alocação. Se a JVM detecta que o atual uso de memória excede o limite de uso, ele irá definir o UsageThresholdExceeded atributo para true.

Alguns pools de memória pode não suportar o limite de uso. Você pode usar o UsageThresholdSupported atributo para determinar se um pool de memória suporta um limite de uso. Por exemplo, em um coletor de lixo de gerações (como a máquina virtual HotSpot), a maioria dos objetos são alocados na geração jovem, a partir da “memória de pool eden”. A piscina eden é projetado para ser preenchido, da coleta de lixo na área de memória eden livre arbítrio mais do seu espaço de memória, já que espera-se que contêm principalmente objetos de vida curta inacessível no momento de coleta de lixo. Assim, tendo o conjunto de memória eden para apoiar o nível de utilização não é apenas não úteis, mas também não pode ser aplicado eficientemente.

Coleção limite de uso

Coleção limite de uso é um atributo gerenciável de alguns recolhidos de memória piscinas-lixo. Depois de uma JVM tem realizado a coleta de lixo em um pool de memória, parte da memória na piscina ainda vai ser ocupado por objetos acessíveis. O limite de uso da coleção permite que você defina um valor para verificar contra o uso de memória somente após a coleta de lixo. Se a JVM detecta que o uso de memória excede o limite de uso da coleção, que vai definir o CollectionUsageThresholdExceeded atributo para true.

Você pode usar o CollectionUsageThresholdSupported atributo para determinar se a piscina suporta um limite de uso de coleta.

O limite de uso e limite de uso da coleção é definida na guia MBeans. Por exemplo, selecione o TenuredGen pool de memória na árvore à esquerda, defina o nível de utilização da geração de memória de pool titulares e 6 Mbytes.

Figure 6: Setting Usage Threshold

Figura 6: nível de utilização Ambiente.

Quando o uso de memória do TenuredGen pool de memória exceder 6 MBytes, parte da barra que representa a TenuredGen pool de memória ficará vermelho para indicar a porção de memória usada, que excedam o limite de uso. A barra que representa a pilha de memória também ficam vermelhos. Você pode clicar na barra ou seleccionar um conjunto de memória específica no menu Gráfico para mudar para a informação sobre um conjunto de memória específica. Se você passar o cursor sobre um bar, o nome do pool de memória que ele representa será exibido.

Figure 7: Low Memory

Figura 7: Memória Baixa.

Ativar ou desativar o rastreamento detalhado VM

Conforme descrito anteriormente, o sistema de memória MBean define um atributo booleano chamado detalhado que permite que você rode o rastreamento detalhado GC ou desativar dinamicamente. Os traços da GC detalhada será apresentada no local especificado na inicialização da JVM. O local padrão para GC saída detalhada da máquina virtual Hotspot é stdout.

Para habilitar ou desabilitar o rastreamento detalhado GC, selecione a memória MBean e definir o atributo verbal para verdadeiro ou falso. Da mesma forma, a classe de carga MBean também tem o atributo detalhado, que pode ser configurado para ativar ou desativar o carregamento de classes detalhado rastreamento.

Figure 8: Setting Verbose GC

Figura 8: GC Verbose Ambiente.

Detecção de Deadlocks

A guia Tópicos fornece informações sobre threads em execução em um aplicativo.

Figure 9: Threads Tab

Figura 9: Guia Threads.

A lista de tópicos nas listas canto inferior esquerdo todos os segmentos ativos. Se você digitar um texto no campo Filtro, na lista Tópicos vai mostrar apenas as linhas cujo nome contenha a string que você entrar. Você pode obter o despejo de discussão de um tópico clicando no nome de uma discussão na lista Tópicos.

O MBean Threading fornece várias outras operações úteis que não são cobertos pela aba Threads:

  • findMonitorDeadlockedThreads. Detecta se algum tópicos estão num impasse sobre os bloqueios monitor objeto. Esta operação retorna uma matriz de IDs thread bloqueada.
  • getThreadInfo. Retorna as informações de discussão. Isso inclui o nome, o rastreamento de pilha, e monitorar o bloqueio que o segmento está bloqueado no, se houver, e qual o segmento está mantendo esse bloqueio, e as estatísticas de contenção de discussão.
  • getThreadCpuTime. Retorna o tempo de CPU consumido por um determinado segmento

Para acessar esses recursos adicionais, vá para a aba de MBeans e selecione o MBean Threading na árvore de MBeans. Ele lista todos os atributos e operações para acessar informações na JVM está sendo monitorado.

Figure 10: MBeans Tab Threading

Figura 10: Guia MBeans Threading.

Para verificar se seu aplicativo foi executado em um beco sem saída (por exemplo, o aplicativo parece ser pendurado), você pode chamar ofindMonitorDeadlockedThreads operação.

Figure 11: Find Deadlocked Threads

Figura 11: Encontre Tópicos impasse.

Uma vez que você clica no findMonitorDeadlockedThreads botão, a Operação Retorno janela valor aparece para mostrar o resultado. No exemplo acima, JConsole se conecta ao aplicativo de exemplo Sampletest, que tem três tópicos em um impasse. Ele detecta que segmentos de ID 12, 10 e 11 estão num impasse, como mostrado na figura anterior. Para mais informações sobre as linhas bloqueadas, você pode usar ogetThreadInfo operação. O MBean Threading apoia a getThreadInfo operação em quatro formas diferentes, que obtêm informações área:

  • De uma identificação com determinado segmento de rastreamento de pilha o número máximo especificado de quadros.
  • De uma matriz de IDs de discussão com a pilha de rastreamento do número máximo especificado de quadros.
  • De um ID determinado segmento, sem rastreamento de pilha.
  • De uma matriz de IDs de rosca sem rastreamento de pilha.

Para uma situação de impasse, normalmente você estar interessado no rastreamento de pilha. Você pode inserir a ID da thread de um thread bloqueada no primeiro parâmetro da operação getThreadInfo (digamos, ID = 12) eo número de quadros que você deseja obter como o segundo parâmetro (profundidade = 5).

Figure 12: ThreadInfo for thread ID = 12

Figura 12: ThreadInfo por Thread ID = 12.

Clicando duas vezes sobre o campo do valor do atributo stackTrace irá mostrar uma visão composta de navegação que lhe permite percorrer o rastreamento de pilha. Figuras 13 e 14 mostram o Composite vista de navegação que exibe a segunda parte superior e quadro superior do rastreamento de pilha de DeadlockedThread-1.

Figure 13: Top Frame of the Stack Trace of DeadlockedThread-1

Figura 13: Estrutura topo da pilha de rastreamento de DeadlockedThread-1.

Figure 14: Second Frame of the Stack Trace of DeadlockedThread-1

Figura 14: segundo quadro do rastreamento de pilha de DeadlockedThread-1.

A guia Tópicos fornece uma maneira mais amigável do usuário para olhar para o rastreamento de pilha de um thread. Você pode encontrar o nome do impasse tópicos usando o getThreadInfo operação. Então você pode usar a guia Threads para analisar o impasse:

  • DeadlockedThread-1 é bloqueada para introduzir um monitor de bloqueio de propriedade DeadlockedThread-2
  • DeadlockedThread-2 está bloqueado para entrar em outro monitor de bloqueio de propriedade DeadlockedThread-3
  • DeadlockedThread-3 está bloqueado para entrar em outro monitor de bloqueio de propriedade DeadlockedThread-1

Controle de Log

O MBean registro define um LoggerNames atributo que descreve a lista de nomes do registrador. Para encontrar a lista de madeireiros em seu aplicativo, selecione o registro sob o MBean java.util.logging domínio na árvore MBeans e clique duas vezes no campo de valor da LoggerNamesatributo.

Figure 15: List of All Logger Names

Figura 15: Lista de todos os nomes Logger.

O MBean Log também suporta três operações:

  • getParentLoggerName. Retorna o nome do pai de um logger registrador dado.
  • getLoggerLevel. Retorna o nível de log de um registrador de dados.
  • setLoggerLevel. Define o nível de log de um registrador de dados para um novo nível.

Todas as três operações têm um nome madeireiro como o primeiro parâmetro. Para alterar o nível de um madeireiro, digite o nome do logger no primeiro parâmetro eo nome do nível deve ser definido no segundo parâmetro da setLoggerLevel operação e, em seguida digite osetLoggerLevel botão. A Sampletest aplicativo tem seu próprio madeireiro com.sun.example.hello, que está incluído na lista anterior de nomes de registrador. Você pode chamar o setLoggerLevel operação para definir a com.sun.example.hello logger para FINEST nível. Uma janela pop-up aparece, mostrando se o método é invocado com sucesso ou se houver um erro.

Figure 16: Setting Log Level

Figura 16: Nível de registro Ambiente.

Acessando OS Extensão Recursos Sun-Plataforma

O JDK 5.0 estende o sistema operacional MBean para incluir informações sobre os recursos certos mínimos, tais como:

  • o tempo de CPU processo
  • a quantidade total de memória física e livre
  • a quantidade de memória virtual comprometida (isto é, a quantidade de memória virtual garantido que estará disponível para o processo de execução)
  • a quantidade de espaço de troca livre e total
  • o número de descrições de arquivos abertos (UNIX apenas)

Quando o Sistema Operacional MBean na guia MBeans for selecionada, você vê todos os atributos e operações, incluindo a extensão da plataforma. Você pode acompanhar as mudanças de um atributo numérico ao longo do tempo, por exemplo, o processo de tempo de CPU por um duplo clique no campo de valor do atributo.

Figure 17: MBeans Tab OS

Figura 17: Guia OS MBeans.

Além disso, o guia VM eo guia Resumo fornecer informações sobre os recursos do sistema operacional.

Gerenciando MBeans Aplicação

A Sampletest aplicação que está sendo monitorado tem suas próprias Olá MBean com o nome do objeto:

   type = Olá: com.sun.example

Se o atributo CacheSize é alterado, a Olá MBean enviará uma notificação. Você pode usar a guia MBeans para gerenciar seu aplicativo MBeans, bem como os MBeans plataforma. Por exemplo, você pode querer controlar quando o atributo CacheSize é alterado. Você primeiro subscrever as notificações na guia Notificação. Se você alterar o CacheSize para 300, você verá uma notificação enviada.

Figure 18: Notifications

Figura 18: Notificações.

Resumo

A ferramenta JConsole demonstra a abrangência e facilidade de utilização do monitoramento do JDK 5.0 e recursos de gerenciamento. Você pode usar JConsole para se conectar a uma máquina virtual Java rodando, e em seguida, monitorar o uso da memória e da atividade de discussão. Você pode obter o carregamento da classe de informação, além de informações sobre a JVM e sistema operacional. JConsole também lhe dá uma forma genérica para acesso MBeans, de modo que você pode acessar o conjunto completo de instrumentos da plataforma Java e também gerenciar seu aplicativo.

Nós descrevemos como acessar várias centrais de monitoramento e funcionalidades sob a gestão da plataforma Java através JConsole, incluindo a detecção de falta de memória, detalhado máquina virtual de rastreamento, a detecção de bloqueio e controle de nível de registrador.

Informações relacionadas

Sobre o Autor

Mandy Chung é um engenheiro sênior do Grupo de Manutenção Java na Sun Microsystems. Ela projetou e implementou o monitoramento JVM e interface de gerenciamento em JDK 5.0 e liderou o desenvolvimento JConsole. Ela possui um mestrado em Ciência da Computação da Universidade da Califórnia, em Davis, e bacharel em Computer Studies da Universidade de Hong Kong.

No comments yet.