Avançar para a área de conteúdo

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

A primeira vez que acessar o developerWorks, um perfil será criado para você. Informações do seu perfil (tais como: nome, país / região, e empresa) estarão disponíveis ao público, que poderá acompanhar qualquer conteúdo que você publicar. Seu perfil no developerWorks pode ser atualizado a qualquer momento.

Todas as informações enviadas são seguras.

  • Fechar [x]

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Todas as informações enviadas são seguras.

  • Fechar [x]

Java em Tempo Real, Parte 6: Simplificando o Desenvolvimento de Java em Tempo Real

Apresentando o padrão Lifecycle Memory Managed Periodic Worker Threads

Michael Dawson, Advisory Software Developer, IBM Ottawa Lab
Michael Dawson
Michael Dawson formou-se pela University of Waterloo em 1989 como bacharel em engenharia de computação e, em 1991, na Queens University, obteve seu mestrado em engenharia elétrica, especializando-se em criptografia. Depois disso, passou diversos anos como consultor em segurança, desenvolvendo produtos em empresas que iam de empresas novas até a IBM. Teve funções de liderança em equipes de desenvolvimento de aplicativos de e-commerce e na entrega dos mesmos como serviços, incluindo serviços de comunicação EDI, processamento de cartão de crédito, leilões on-line, faturamento eletrônico e JVMs. As tecnologias usadas variavam de C/C++ a Java e plataformas e componentes Java EE através de uma variedade de sistemas operacionais. Michael entrou na IBM em 2006. Ele trabalha na equipe J9 JVM que implementa bibliotecas de classe Java e componentes principais de JVM.

Resumo:  Agora que as Java™ virtual machines em tempo real suportam memória com escopo definido, definir padrões comuns para o uso de memória com escopo definido pode melhorar a produtividade do desenvolvedor. Esses padrões reduzem a necessidade de entender ou trabalhar com escopos diretamente, fornecendo as funções principais does escopos com menos complexidade. Este artigo, o sexto e último da série Java em Tempo Real , apresenta o padrão Lifecycle Memory Managed Periodic Worker Threads como um modelo para simplificar o desenvolvimento de Java em tempo real. Ele demonstra a viabilidade do padrão por meio de uma implementação de amostra e um aplicativo de exemplo simples.

Visualizar mais conteúdo nesta série

Data:  16/Set/2011
Nível:  Intermediário Também disponível em :   Inglês
Atividade:  1185 visualizações
Comentários:  


Real-time Specification for Java (RTSJ) fornece extensões para facilitar o desenvolvimento de aplicativos em tempo real (RT) usando a linguagem Java. Uma extensão desse tipo é um mecanismo para aplicativos para gerenciar áreas de memória fora do heap e para evitar atrasos que são causados por coleta de lixo (GC). Essas áreas de memória são referidas como memória com escopo definido e imortal . Para assegurar a integridade do heap e da área da memória imortal, RTSJ define regras para usar a memória com escopo definido. Essas regras controlam os relacionamentos permitidos entre escopos e as referências permitidas de um objeto em um escopo para objetos em outras áreas de memória. As regras permitem que escopos sejam usados em uma ampla gama de padrões e aplicativos de design, mas essa flexibilidade vem com o custo de complexidade. Como resultado, alguns desenvolvedores acham o uso de escopos difícil e vulnerável a erros. Este artigo apresenta uma técnica que pode ser usada para simplificar o uso da memória com escopo definido.

O padrão Lifecycle Memory Managed Periodic Worker Threads

Um requisito comum em um sistema RT é um conjunto de encadeamentos que são executados periodicamente e cooperam para concluir algumas tarefas. RTSJ inclui mecanismos específicos para executar encadeamentos de maneira periódica. O padrão Lifecycle Memory Managed Periodic Worker Threads (LMMPWT) tem como objetivo fornecer um modelo de gerenciamento de memória simplificado que é adequado para uma faixa de casos de uso em que encadeamentos periódicos não devem ser interrompidos por GC.

Requisitos do padrão LMMPWT

Para ser aplicável a aplicativos do mundo real, o padrão LMMPWT deve atender pelo menos os seguintes requisitos:

  • Acomodar um ou mais encadeamentos trabalhando juntos para realizar uma tarefa.
  • Fornecer execução sem interrupção por GC.
  • Fornecer um mecanismo para que os encadeamentos em cooperação se comuniquem.
  • Fornecer gerenciamento de ciclo de vida para objetos.

Modelo de gerenciamento de memória

O padrão LMMPWT restringe os tempos de vida dos objetos disponíveis àqueles mais úteis para um encadeamento periódico, enquanto minimiza o tempo de vida dos objetos e o total de memória necessário. Os tempos de vida de objetos suportados são:

  • Retain Forever: Objetos com esse tempo de vida nunca são coletados como lixo e estão acessíveis para todos os encadeamentos.

  • Retain Thread Group: Objetos com esse tempo de vida estão disponíveis enquanto qualquer um dos encadeamentos de um grupo de encadeamentos em cooperação não tiver sido finalizado. Esses objetos estão acessíveis somente por encadeamentos dentro do grupo de encadeamentos.

  • Retain Thread: Objetos com esse tempo de vida estão disponíveis somente enquanto um encadeamento específico está em execução e estão acessíveis somente para esse encadeamento.

  • Retain Iteration: Objetos com esse tempo de vida estão disponíveis enquanto um encadeamento está executando uma iteração específica de sua tarefa. Esses objetos estão acessíveis para esse encadeamento somente durante a iteração na qual são criados.

Esse modelo de gerenciamento de memória se ajusta ao ciclo de vida de um conjunto típico de encadeamentos periódicos em cooperação para concluir um conjunto de tarefas. A lógica para cada encadeamento segue o modelo executar>esperar pelo próximo período>executar>esperar pelo próximo período, etc. Durante cada execução (ou, como irei referir a ela agora, iteração), o encadeamento pode alocar diversos objetos. Alguns objetos são necessários somente durante essa própria iteração, alguns são necessários em iterações subsequentes, alguns podem precisar ser compartilhados com outros encadeamentos no grupo de encadeamentos em cooperação e alguns podem precisar ser compartilhados fora desse grupo. O modelo suporta esses requisitos permitindo que o desenvolvedor crie objetos com o tempo de vida apropriado.

Esse modelo simplifica o design do aplicativo. Em vez de precisar considerar quais escopos são necessários, você simplesmente decide quanto tempo o objeto deve ser retido. Isso limita a granularidade de gerenciamento de memória, mas, na maioria dos casos, a simplicidade, a sustentabilidade e a facilidade de desenvolvimento superam as vantagens de um controle mais fino. Espera-se que a maioria dos objetos necessários para os aplicativos será alocada com um tempo de vida Retain Iteration, indicando que serão liberados na conclusão de cada iteração.

Para manter a integridade de objetos com diferentes tempos de vida, você deve seguir uma regra ao fazer referência a objetos: um objeto não pode fazer referência a outro objeto com um tempo de vida mais curto. Isso e consistente com as regras para escopos definidas em RTSJ e o cumprimento pode ser realizado pela implementação do escopo subjacente. A regra está resumida na Tabela 1:


Tabela 1. Regra para fazer referência a objetos com tempos de vida diferentes
Tempo de vida de objeto que contém referênciaReferência permitida a objeto com Retain Forever?Referência permitida a objeto com Retain Thread Group?Referência permitida a objeto com Retain Thread?Referência permitida a objeto com Retain Iteration?
Retain ForeverSimNãoNãoNão
Retain Thread GroupSim

Sim

Não

Não

Retain Thread

Sim

Sim

Sim

Não

Retain Iteration

Sim

Sim

Sim

Sim

Um elemento chave de qualquer modelo de gerenciamento de memória está controlando quando objetos são coletados e a memória é retornada ao sistema. A Tabela 2 descreve esse aspecto do modelo para cada um dos tempos de vida de objetos suportados:


Tabela 2. Quando objetos são coletados e memória é retornada para cada tempo de vida de objeto
Tempo de vida de objetoQuando objetos são coletados e memória é recuperada

Retain Forever

Nunca.

Retain Thread Group

Quando todos os encadeamentos de um grupo de encadeamentos tiverem sido finalizados, todos os objetos com esse tempo de vida são coletados e a memória alocada para esse grupo é retornada ao sistema.

Retain Thread

Quando um encadeamento é finalizado, todos os objetos com esse tempo de vida são coletados e a memória alocada para esse encadeamento é retornada ao sistema.

Retain Iteration

Quando a iteração atual para um encadeamento é finalizada, todos os objetos com esse tempo de vida são coletados. A área de memória permanece comprometida com o encadeamento para a próxima iteração. Quando o encadeamento concluir sua última iteração e for finalizado, a memória alocada para esse encadeamento é retornada ao sistema.

Gerenciando objetos

Você deve ser capaz de configurar os tempos de vida para objetos alocados. Para minimizar o trabalho necessário por objeto, o padrão deve minimizar os objetos para os quais você deve especificar o tempo de vida do objeto. Retain Iteration para ser o mais adequado para o padrão, porque você deve reter objetos além de uma iteração somente se o aplicativo precisar disso. Usar Retain Iteration permite ignorar objetos temporários que o aplicativo cria e que são criados por métodos que ele chama. Para objetos que devem ser retidos, é necessário identificar e entender o requisito para esses objetos de qualquer forma, portanto, especificar seus tempos de vida será um pequeno incremento do trabalho já necessário.

No caso geral, você criar um objeto usando:

Object ref = new Object();

O ideal é poder usar o mesmo código com alguma marcação adicional, como neste exemplo:

/*[Lifetime=Retain Thread]*/
Object ref = new Object();

O melhor mecanismo pode depender de seu ambiente e poderia ser implementado usando-se, por exemplo, pré-processadores, anotações ou objetos auxiliares. O modelo não depende do mecanismo selecionado; no entanto, o mecanismo deve permitir especificar o tempo de vida sem precisar que você entenda escopos e deve evitar desordenação do código para evitar que a sustentabilidade seja afetada. Discutirei uma possível abordagem na seção Implementação usando escopos deste artigo.

Também é necessário poder armazenar referências a objetos de diferentes tempos de vida. Especificamente, você irá querer poder criar objetos com um tempo de vida Retain Thread ou Retain Thread Group em uma iteração e acessá-los em uma iteração subsequente. As regras para referências entre objetos com diferentes tempos de vida pode proibir que variáveis de membros no próprio objeto de encadeamento contenham referências a esses objetos. O modelo precisa incluir um mecanismo para passar objetos raízes a cada iteração que pode ser usada para armazenar e recuperar objetos que têm um tempo de vida Retain Thread Group ou Retain Thread.


Implementação usando escopos

É possível implementar o modelo proposto usando escopos RTSJ padrão e a área de memória imortal. Escopos são usados para suportarem objetos com os tempos de vida definidos, conforme indicado na Tabela 3:


Tabela 3. Área de memória usada para cada tempo de vida de objeto
Tempo de vida de objetoÁrea de memória usada

Retain Forever

Imortal.

Retain Thread Group

Escopo de nível superior alocado quando um grupo de encadeamentos é iniciado.

Retain Thread

Escopo alocado para encadeamento quando o encadeamento é iniciado e isso permanece na pilha de escopo de encadeamentos até ser finalizado.

Retain Iteration

Escopo alocado para encadeamento quando o encadeamento é alocado. É realizada entrada e saída do escopo para cada iteração para que seja liberado entre iterações.

Para um encadeamento executando uma iteração, a pilha de escopo seria conforme mostrada na Figura 1:


Figura 1. Pilha de escopo

Implementação de amostra

As classes de API de amostra a seguir demonstram o uso do modelo proposto usando essa implementação baseada em escopo:

  • PeriodicThread
  • PeriodicThreadGroup
  • ObjectRootsHelper
  • PeriodicThreadDescriptor
  • StartPeriodicThreads

PeriodicThread é a classe base a partir da qual encadeamentos do trabalhador do conjunto de encadeamentos em cooperação devem ser derivados. Gerencia os escopos em segundo plano. Define as constantes a seguir, que são usadas para especificar tempos de vida de objetos:

  • RETAIN_FOREVER
  • RETAIN_THREAD_GROUP
  • RETAIN_THREAD
  • RETAIN_ITERATION

O método de execução nessa classe é final de forma que as classes derivadas não podem usá-la para especificar sua lógica. Em vez disso, você deve substituir o método a seguir para que contenha a lógica para o encadeamento:

threadLogic(ObjectRootsHelper helper)

Esse método é chamado toda vez que o encadeamento precisar executar a lógica para uma iteração.

Uma instância de ObjectRootsHelper é passada para ajudar o encadeamento a gerenciar objetos cujos tempos de vida excedem a iteração. Como o próprio encadeamento pode ser alocado fora de um escopo (neste caso, o escopo para o Thread Group), pode não ser possível usá-lo para armazenar referências a objetos com determinados tempos de vida. ObjectRootsHelper fornece os métodos a seguir, que podem ser usados para armazenar e recuperar referências a esses objetos:

  • void add(int lifetime, String key,Object obj)
  • void addSynchronized(int lifetime,String key, Object obj)
  • Object get(int lifetime, String key);
  • Object getSynchronized(lifetime,String key);

ObjectRootsHelper suporta gerenciar objetos com tempos de vida RETAIN_THREAD_GROUP e RETAIN_THREAD.

Como a lógica em threadLogic é chamada para cada iteração, o encadeamento precisa de um mecanismo para indicar que concluiu sua última iteração e deve ser finalizado. O método setTerminate() é fornecido de forma que o encadeamento possa indicar à estrutura quando deseja finalizar.

PeriodicThread também inclui o método a seguir:

public Object allocate(final Class classObject, 
                       int lifespan, 
                       final Object ... parms)
   throws IllegalArgumentException

O método allocate() permite alocar um objeto com um determinado tempo de vida. Por exemplo, quando nenhum parâmetro for requerido:

token = (WatchedThreadToken) allocate(WatchedThreadToken.class,
                                      PeriodicThread.RETAIN_THREAD_GROUP);

Ou quando parâmetros forem requeridos:

watchedThreads = (WatchedThreads) allocate(WatchedThreads.class,
                                           PeriodicThread.RETAIN_THREAD,
                                           new PrimInt(10),
                                           new PrimInt(3));

Observe a maneira como primitivas são passadas. Listas de argumentos com comprimento variável requerem que você passe com.ibm.realtime.easyrtj.prmitive.PrimXXX em vez de a própria primitiva quando o construtor da classe incluir parâmetros de primitiva (long, int, float, double, short).

Se um objeto criado por um encadeamento precisar realizar alocação internamente, você deve passar uma referência ao próprio encadeamento ao objeto para que os métodos allocate() possam ser chamados quando necessário.

Um único construtor é fornecido para PeriodicThread:

PeriodicThread(SchedulingParameters scheduling, 
               RelativeTime period,
               Long threadMemorySize,
               Long iterationMemorySize,
               PeriodicThreadGroup group)

Esse construtor é público, porque PeriodicThread deve ser subclassificado. No entanto, não é esperado chamar o construtor diretamente. As classes auxiliares PeriodicThreadDescriptor e StartPeriodicThreads são fornecidas de forma que não seja necessário construir PeriodicThreads diretamente.

A classe PeriodicThreadDescriptor descreve um encadeamento para ser iniciado como parte de um grupo. Ela fornece um único construtor público:

PeriodicThreadDescriptor(int priority, 
                         Class classObject, 
                         long threadMemorySize, 
                         long iterationMemorySize, 
                         long period) 

Esse construtor permite especificar a prioridade, os tamanhos de memória e o período para um encadeamento, juntamente com a classe que representa o encadeamento a ser executado (uma classe que estende PeriodicThread).

Você desenvolver um array de PeriodicThreadDescriptors e passa os mesmos ao método startThreads estático de StartPeriodicThreads juntamente com o tamanho da memória a ser compartilhada entre os encadeamentos. startThreads iniciam os encadeamentos conforme definido pelos descritores de forma que compartilhem uma memória do tamanho indicado (ou seja, compartilhem objetos com um tempo de vida RETAIN_THREAD_GROUP).

Em suma, para usar as classes de amostra, você:

  1. Cria uma ou mais classes que subclassificam PeriodicThread, colocando a lógica necessária no método threadLogic .
  2. Cria um PeriodicThreadDescriptor para cada encadeamento a ser executado como um grupo.
  3. Inicia os encadeamentos chamando startThreads, passando o array de descritores.

Código de segurança de amostra

Um caso de uso de RT típico é ter um grupo de encadeamentos em cooperação para realizar um conjunto de tarefas e para usar uma segurança para assegurar que os encadeamentos continuem ativos e realizem suas tarefas. Cada encadeamento deve notificar periodicamente a segurança para indicar que ainda está ativo. Um encadeamento de monitoramento emite um alarme se qualquer encadeamento não se apresentar dentro de um intervalo de tempo determinado.

O código de segurança de amostra (consulte Download) define WatchedPeriodicThread como uma subclasse de PeriodicThread. O método threadLogic dessa classe é final e subclasses colocam a lógica a ser executada no método watchedLogic . ObjectRootsHelper passado para threadLogic é passado para o método watchedLogic . Além disso, as subclasses devem implementar getThreadName para retornar o nome exclusivo a ser associado ao encadeamento.

Em threadLogic para WatchedPeriodicThread, código é incluído para criar um objeto WatchedThreadToken com um tempo de vida RETAIN_THREAD_GROUP. Ele é então compartilhado com outros encadeamentos armazenando-o com ObjectRootsHelper, com o nome do encadeamento como a chave. Toda vez que threadLogic é executado, executa o método watchedLogic fornecido pela subclasse e, em seguida, chama kick em seu WatchedThreadToken para indicar que o encadeamento ainda está ativo.

WatchDogThread aloca um objeto WatchedThreads com um tempo de vida RETAIN_THREAD e usa o mesmo para controlar a atividade dos encadeamentos que está acompanhando. Toda vez que threadLogic é chamado para WatchDogThread, valida se os encadeamentos que está acompanhando ainda estão ativos e emite um alarme se qualquer encadeamento estiver inativo. (Simplesmente usa printf, mas isso pode facilmente enviar um trap SNMP, por exemplo.)

Thread1 é um encadeamento periódico (estende WatchedPeriodicThread) que é executado por 10 iterações e então fica inativo devido a uma exceção "inesperada". Thread2 é um encadeamento periódico simples (estende WatchedPeriodicThread) que continua a ser executado para sempre.

A classe TestMain usa StartPeriodicThreads.startThreads para iniciar Thread1, Thread2 e WatchDogThread. Executar o programa de teste mostra que WatchDogThread começa a monitorar Thread1 e Thread2 e, em seguida, emite um alarme quando Thread1 fica inativo.

Esta amostra demonstra o uso do modelo com objetos que têm tempos de vida RETAIN_THREAD_GROUP, RETAIN_THREAD e RETAIN_INTERATION (o padrão).

O download fornece um arquivo JAR que inclui a origem e as classes compiladas para a implementação de amostra e o aplicativo de segurança. Para executar a amostra, use a linha de comando a seguir com uma máquina virtual RT:

java -cp LMMPWTSample.jar com.ibm.realtime.easyrtj.sample.TestMain


Conclusão

Abordagens alternativas, como coletores de lixo RT, reduzem a necessidade de usar escopos, mas é provável que escopos ainda sejam necessários em alguns aplicativos. Este artigo apresentou o padrão LMMPWT para simplificar uso de memória com escopo definido. Para demonstrar a viabilidade e utilidade do padrão, descreveu uma implementação de amostra usando escopos e forneceu um aplicativo de amostra mostrando o uso do modelo com objetos dos vários tempos de vida.



Download

DescriçãoNomeTamanhoMétodo de download
Sample source codej-rtj6.jar85KBHTTP

Informações sobre métodos de download


Recursos

Aprender

Obter produtos e tecnologias

  • WebSphere Real Time: O WebSphere Real Time permite que aplicativos dependentes de tempos de resposta precisos aproveitem a tecnologia Java padrão sem sacrificarem determinismo.

  • Real-time Java technology: Visite esse site de pesquisa IBM alphaWorks® para encontrar tecnologias de ponta para RT Java.

Discutir

Sobre o autor

Michael Dawson

Michael Dawson formou-se pela University of Waterloo em 1989 como bacharel em engenharia de computação e, em 1991, na Queens University, obteve seu mestrado em engenharia elétrica, especializando-se em criptografia. Depois disso, passou diversos anos como consultor em segurança, desenvolvendo produtos em empresas que iam de empresas novas até a IBM. Teve funções de liderança em equipes de desenvolvimento de aplicativos de e-commerce e na entrega dos mesmos como serviços, incluindo serviços de comunicação EDI, processamento de cartão de crédito, leilões on-line, faturamento eletrônico e JVMs. As tecnologias usadas variavam de C/C++ a Java e plataformas e componentes Java EE através de uma variedade de sistemas operacionais. Michael entrou na IBM em 2006. Ele trabalha na equipe J9 JVM que implementa bibliotecas de classe Java e componentes principais de JVM.

Ajuda para Relatar Abuso

Relatar abuso

Obrigado. Esta entrada foi sinalizada para atenção do moderador.


Ajuda para Relatar Abuso

Relatar abuso

Falha no envio do Relatório de abuso. Tente novamente mais tarde.


developerWorks: Registre-se


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Selecione seu nome de exibição

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

(Deve possuir de 3 a 31 caracteres.)


Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Classificar este artigo

Comentários

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java
ArticleID=757367
ArticleTitle=Java em Tempo Real, Parte 6: Simplificando o Desenvolvimento de Java em Tempo Real
publish-date=09162011
author1-email=michael_dawson@ca.ibm.com
author1-email-cc=