Lapsed Listeners
October 6, 2009 on 1:55 pm | In java | No CommentsSó uma nota muito rápida a propósito de uma memory leak que tive que identificar recentemente.
Até encontrar esta situação, todos os casos com que me tinha deparado eram sempre causados por uma colecção static que não estava a ser devidamente mantida. Esta situação, é ligeiramente diferente, no sentido em que a colecção em causa não é mantida directamente pela aplicação, mas sim pelo SWT. É um problema conhecido como “Lapsed Listeners” e está bem descrito aqui:
http://www.javaworld.com/javaworld/javatips/jw-javatip79.html?page=1
Resumidamente: lembrem-se sempre de remover os listeners que já não são acessíveis, ou então implementem-nos com uma WeakReference.
Application Bundle Info.plist
August 24, 2009 on 5:25 pm | In mac, java | No CommentsTenho estado a portar uma aplicação Java/SWT para Mac OSX e as coisas até têm estado a correr bem (depois de me ter resignado a usar Carbon em vez de Cocoa…). Isto claro, até me passar pela cabeça que precisava de alterar o comportamento do .app que o Product Export Wizard do Eclipse estava a gerar.
Basicamente queria que em vez de ser invocado o executável que lança a JVM fosse primeiro chamado um shell script para fazer umas validações antes de chamar o executável. Pareceu-me que a forma correcta de o fazer seria editar o ficheiro:
Info.plist
dentro do application bundle, nomeadamente a propriedade:
CFBundleExecutable
que como o próprio nome sugere, indica qual é o executável que deve ser lançado quando o utilizador faz duplo-clique na aplicação em causa.
Infelizmente, por muito que alterasse a bendita propriedade, nada. O sistema continuava tranquilamente a invocar o executável. Aparentemente a alteração estava a ser ignorada por algum motivo.
Não foi fácil de encontrar, mas já alguém tinha tentado fazer o mesmo e obtido o mesmo resultado:
Hi,
I’m trying to customize the behavior of certain application bundles by editing the Info.plist preferences. The idea is to change the CFBundleExecutable key to point to a different file. Unfortunately, my changes seem to be ignored. — by Chadrik in Apple Support Forums
E felizmente alguém tinha respondido:
Some of those things are only read at startup or logiin, hence no change will happen until you logout or restart. — by BDAquain Apple Support Forums
Enfim, só foi pena as horas perdidas… as caches são umas gajas lixadas… sobretudo quando não se sabe que elas existem
Mas ainda assim, não há quem me convença que a coisa está mal feita… seria assim tão difícil ao leopardo, no momento em que guarda o ficheirito em cache registar-se na FAM (ou no kqueue que parece ser a alternativa em Mac / BSD…) para receber notificações de alteração?
Thread-safe singleton?
March 11, 2009 on 12:18 pm | In performance, java | No CommentsUma das piores coisas que se pode encontrar quando se pretende migrar uma aplicação single-threaded para multi-threaded é um Singleton que nunca o deveria ter sido, um daqueles que guarda informação de estado por todo o lado e que é usado em múltiplos sítios da aplicação ao longo de todo o ciclo de vida.
O que é se faz a uma coisa destas? A solução parece óbvia: “alteras o método instance() para devolver sempre um objecto novo”, efectivamente desfazendo o comportamento de singleton. Ok, funciona, mas e se o impacto em termos de performance for demasiado elevado? O que fazer quando se pretende manter o comportamento de singleton, mas ao nível do Thread, em vez de ser ao nível da JVM?
A resposta é substituir as variáveis existentes que guardam informação de estado por outras do tipo ThreadLocal<T>.
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
Ok, perfeito, não é?
Hmm… quase. Funciona bem se a criação dos threads for feita exclusivamente “antes” da primeira utilização da classe. Dessa forma cada thread inicializa a sua própria cópia uma só vez e trabalha com ela daí para a frente.
Mas e se forem necessários vários níveis de multi-threading, alguns dos quais “depois” da primeira utilização da classe? De acordo com a forma de funcionamento das variáveis ThreadLocal, cada thread filho teria uma instância nova, o que pode corresponder a esforço desnecessário e, dependendo dos casos, ter um impacto significativo no desempenho.
O nome diz tudo: InheritableThreadLocal<T> é uma subclasse de ThreadLocal<T> que permite herdar a instância do thread pai se ela existir.
This class extends ThreadLocal to provide inheritance of values from parent thread to child thread: when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values. Normally the child’s values will be identical to the parent’s; however, the child’s value can be made an arbitrary function of the parent’s by overriding the childValue method in this class.
Agora sim, perfeito. ![]()
JDBC e Reverse DNS Lookups
March 9, 2009 on 6:53 pm | In performance, java, windows, networking | No CommentsSe alguma vez ouvirem alguém queixar-se que uma aplicação Java client/server que funciona exclusivamente em rede local fica extraordinariamente lenta cada vez que o acesso à Internet cai, lembrem-se deste post.
Com uma descrição destas a primeira coisa que me ocorre é DNS. Como a aplicação não deixa de funcionar, fica simplesmente mais lenta, provavelmente o problema estará numa resolução inversa de DNS, que são frequentemente feitas pelas aplicações / middleware apenas para efeitos de logging.
Para não restarem dúvidas que o problema se relacionava com DNS, o servidor em causa tinha a configuração de DNS a apontar para o servidores do ISP, que obviamente ficam inacessíveis cada vez que se perde o acesso à Internet.
Comecei por procurar na aplicação, mas não encontrei nada que justificasse um lookup inverso de DNS. No tomcat encontrei um parâmetro que parecia promissor:
enableLookups - Set to true if you want calls to request.getRemoteHost() to perform DNS lookups in order to return the actual host name of the remote client. Set to false to skip the DNS lookup and return the IP address in String form instead (thereby improving performance). By default, DNS lookups are enabled.
Mas mesmo depois de desactivar o problema persistia. Descendo ainda mais na stack aplicacional descobri este bug no Java, já antiguinho, mas ainda aplicável à versão que estava a ser usada:
There is a call to InetSocketAddress.getHostName() in the ProxySelector code. This will trigger a reverse lookup when the hostname is not already known. If DNS is not configured correctly on the machine, this will generate a long timeout.
Foi este post na mailling list do Postgres que me conduziu ao bug em si e a descrição que fazem do problema é mais fácil de entender (e mais assustadora):
I can confirm that this is an issue in JDK 1.5 (a/k/a Java5) in Windows. This is not the fault of postgres, but like others have suggested, it is with reverse DNS lookup. It affects all TCP connections.
If you connect to an IP address it attempts to look up the name, resulting in these delays.
Se actualizar a versão de Java não for uma hipótese viável (como não era no meu caso), e se a resolução inversa ocorrer sempre para o mesmo endereço IP ou conjunto restrito de endereços, a solução pode ser mesmo adicioná-los ao ficheiro de hosts (C:\windows\system32\drivers\etc\hosts ou /etc/hosts).
Pelo caminho ainda descobri uma forma fácil de ver o conteúdo da cache de DNS num sistema windows:
ipconfig /displaydns
Se ficaram curiosos sobre como fazer a mesma coisa em Linux, a resposta não é fácil. Sem software adicional o kernel Linux não inclui uma cache de DNS, cada pedido é um pedido. Existe o nscd (Name Server Cache Daemon) que é bastante comum mas não vem pré-instalado em todas as distribuições (por exemplo não vinha no último Debian que instalei).
JBoss + Hibernate + MySQL sem perder ligações
November 28, 2008 on 10:29 am | In mysql, java, open-source, database, web | 1 CommentExistem essencialmente duas formas distintas de usar Hibernate com MySQL (ou qualquer outra base-de-dados, para este ponto em concreto é indiferente), definindo todas as propriedades da ligação no próprio ficheiro de configuração do Hibernate, ou dizendo apenas que se pretende usar uma datasource JNDI pré-existente.
A primeira opção é a escolha típica numa aplicação stand-alone, mas a opção de utilizar uma datasource JNDI é a que faz mais sentido para application servers. Portanto, o ficheiro de configuração do Hibernate há-de ter, parcialmente, este aspecto:
<hibernate-configuration>
<session-factory>
<property name=”hibernate.connection.datasource”>
java:my_app_name
</property>
<property name=”hibernate.setup”>true</property>
<property name=”hibernate.dialect”>
org.hibernate.dialect.MySQLInnoDBDialect
</property>
<property name=”transaction.factory_class”>
org.hibernate.transaction.JDBCTransactionFactory
</property>
<property name=”current_session_context_class”>
thread
</property>
</session-factory>
</hibernate-configuration>
Depois basta definir a datasource com o nome JNDI correspondente, por exemplo assim:
<datasources>
<local-tx-datasource>
<jndi-name>my_app_name</jndi-name>
<connection-url>jdbc:mysql://host_or_ip/db_name</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>username</user-name>
<password>password</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>20</max-pool-size>
</local-tx-datasource>
</datasources>
Agora que está tudo montado como manda a documentação, sugiro uma experiência:
- Ligar servidor BD.
- Ligar application server.
- Testar aplicação que se liga à base de dados (funciona)
- Reiniciar servidor BD (ou esperar 8 horas).
- Testar aplicação que se liga à base de dados (não funciona!)
Então… tanto trabalho para abstrair a ligação a base de dados e um simples restart da base de dados deixa as ligações geridas pelo Hibernate todas inválidas? E, pior, não há recuperação automática?
Pois… parece que não. E acrescentar o parâmetro autoReconnect=true ao URL JDBC também não resolve, aliás, a utilização desse parâmetro até é desaconselhada, como se pode ver pelo que diz no manual:
Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for a queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don’t handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly. — MySQL 5.0 Reference Manual
A solução que encontrei para MySQL (e que também funciona para MS SQL Server), é acrescentar a seguinte linha ao ficheiro de configuração da datasource:
<check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
Esta opção faz mesmo aquilo que parece fazer, ou seja, antes de usar uma ligação, a query ali especificada é executada como forma de verificar se a ligação é usável. Se não ocorrer nenhum erro, a instrução SQL que realmente se pretende executar é enviada pela mesma ligação, se houver um erro, a ligação é descartada e é criada uma nova.
Claro que isto tem um impacto sério na performance uma vez que se está a duplicar o número de queries, o que pode ser particularmente grave se o acesso à base de dados for feito através de um canal de elevada latência (rede, por exemplo).
Aparentemente existem soluções melhores para outras bases de dados, como o seguinte comentário parece sugerir:
Whilst you can use the old “select 1 from dual” trick, the downside with this is that it issues an extra query each and every time you borrow a connection from the pool. For high volumes, this is wasteful.
JBoss provides a special connection validator which should be used for Oracle:
org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker
This makes use of the proprietary ping() method on the Oracle JDBC Connection class, and uses the driver’s underlying networking code to determine if the connection is still alive. — stackoverflow.com
Alguém conhece uma alternativa semelhante para MySQL?
© procself. Este blog está alojado no FEUP Blogs.
Crie também o seu blog.
Subscreva os Artigos (RSS)
e os Comentários (RSS) do procself.