Eu tenho um carregador de classes personalizado para que um aplicativo da área de trabalho possa iniciar dinamicamente o carregamento de classes de um AppServer com o qual preciso conversar. Fizemos isso porque a quantidade de frascos necessários para fazer isso é ridícula (se quisermos enviá-los). Também temos problemas de versão se não carregarmos as classes dinamicamente em tempo de execução da biblioteca AppServer.
Agora, acabei de encontrar um problema em que preciso conversar com dois AppServers diferentes e descobri que, dependendo de quais classes eu carrego primeiro, posso quebrar bastante ... Existe alguma maneira de forçar o descarregamento da classe sem realmente matar a JVM?
Espero que isso faça sentido
java
classloader
el_eduardo
fonte
fonte
Respostas:
A única maneira de descarregar uma classe é se o Classloader usado for coletado de lixo. Isso significa que as referências a todas as classes e ao próprio carregador de classes precisam seguir o caminho do dodo.
Uma solução possível para o seu problema é ter um Classloader para cada arquivo jar e um Classloader para cada um dos AppServers que delega o carregamento real de classes para carregadores de classes Jar específicos. Dessa forma, você pode apontar para diferentes versões do arquivo jar para cada servidor de aplicativos.
Isso não é trivial, no entanto. A plataforma OSGi se esforça para fazer exatamente isso, pois cada pacote configurável possui um carregador de classe diferente e as dependências são resolvidas pela plataforma. Talvez uma boa solução seria dar uma olhada nisso.
Se você não deseja usar o OSGI, uma implementação possível pode ser usar uma instância da classe JarClassloader para cada arquivo JAR.
E crie uma nova classe MultiClassloader que estenda o Classloader. Essa classe internamente teria uma matriz (ou Lista) de JarClassloaders e, no método defineClass (), iteraria por todos os classloaders internos até que uma definição fosse encontrada ou uma NoClassDefFoundException fosse lançada. Alguns métodos de acessador podem ser fornecidos para adicionar novos JarClassloaders à classe. Existem várias implementações possíveis na rede para um MultiClassLoader, portanto você pode nem precisar escrever sua própria.
Se você instancia um MultiClassloader para todas as conexões com o servidor, em princípio é possível que cada servidor use uma versão diferente da mesma classe.
Usei a ideia do MultiClassloader em um projeto, onde as classes que continham scripts definidos pelo usuário tinham que ser carregadas e descarregadas da memória e funcionavam muito bem.
fonte
Sim, existem maneiras de carregar classes e "descarregá-las" posteriormente. O truque é implementar seu próprio carregador de classes, que reside entre o carregador de classes de alto nível (o carregador de classes do sistema) e os carregadores de classe dos servidores de aplicativos, e esperar que os carregadores de classes do servidor de aplicativos delegem o carregamento de classe nos carregadores superiores .
Uma classe é definida por seu pacote, seu nome e o carregador de classes que ele carregou originalmente. Programe um carregador de classes "proxy", que é o primeiro carregado ao iniciar a JVM. Fluxo de trabalho:
java.x
esun.x
ao carregador de classes do sistema (eles não devem ser carregados através de nenhum outro carregador de classes que não seja o carregador de classes do sistema).Feito ali, não deve vir um ClassCastException ou LinkageError etc.
Para obter mais informações sobre hierarquias do carregador de classes (sim, é exatamente isso que você está implementando aqui; -) veja "Programação Java baseada em servidor" de Ted Neward - esse livro me ajudou a implementar algo muito semelhante ao que você deseja.
fonte
ClassLoader
por classe seja um pouco demais, umClassLoader
por JAR faz sentido. Poderia ser mais específico sobre como forçar o upload de classe no esquema proposto? Por exemplo, como posso garantir que instâncias de classes carregadas por ClassLoaderA não são referidas por instâncias carregadas por ClassLoaderB?Eu escrevi um carregador de classe personalizado, a partir do qual é possível descarregar classes individuais sem GCing o carregador de classe. Carregador de Classe Jar
fonte
JarClassLoader
para cada arquivo jar que você carregou, poderá chamágetLoadedClasses()
-lo, iterar sobre cada um e descarregá-lo.Os carregadores de classes podem ser um problema complicado. Você pode ter problemas especialmente se estiver usando vários carregadores de classe e não tiver suas interações definidas de forma clara e rigorosa. Eu acho que, para realmente poder descarregar uma classe, você precisará remover todas as referências a quaisquer classes (e suas instâncias) que você está tentando descarregar.
A maioria das pessoas que precisam fazer esse tipo de coisa acaba usando o OSGi . O OSGi é realmente poderoso, surpreendentemente leve e fácil de usar,
fonte
Você pode descarregar um ClassLoader, mas não pode descarregar classes específicas. Mais especificamente, você não pode descarregar classes criadas em um ClassLoader que não está sob seu controle.
Se possível, sugiro usar seu próprio ClassLoader para que você possa descarregar.
fonte
As classes têm uma referência forte implícita à instância do ClassLoader e vice-versa. São lixo coletado como nos objetos Java. Sem acessar a interface das ferramentas ou similar, você não pode remover classes individuais.
Como sempre, você pode obter vazamentos de memória. Qualquer referência forte a uma de suas classes ou carregador de classes vazará a coisa toda. Isso ocorre com as implementações da Sun de ThreadLocal, java.sql.DriverManager e java.beans, por exemplo.
fonte
Se você estiver assistindo ao vivo, se a classe de descarregamento funcionou no JConsole ou algo assim, tente adicionar
java.lang.System.gc()
no final da lógica de descarregamento de classe. Ele dispara explicitamente o Garbage Collector.fonte