Wildfly 10.1.0.FINAL(java.lang.ref.Finalizer/ActiveMQConnection)上的内存泄漏

Wildfly 10.1.0.FINAL(java.lang.ref.Finalizer/ActiveMQConnection)上的内存泄漏,java,memory-leaks,finalizer,finalize,activemq-artemis,Java,Memory Leaks,Finalizer,Finalize,Activemq Artemis,我们有一个JavaWeb应用程序,它通过JMS发送(JobsController.java)和接收消息(JMSMessageListener.java)。在恒定负载下运行应用程序24小时并进行堆转储后,我观察到内存使用量不断增加,应用程序在空闲状态下不会释放内存。我知道这将导致java堆内存不足问题 JobsController是一个ejb无状态bean,它的资源在每次调用后都会被正确销毁。 JMSMessageListener由ejb全局bean池处理,其实例被重用 我从java堆转储中看到的

我们有一个JavaWeb应用程序,它通过JMS发送(JobsController.java)和接收消息(JMSMessageListener.java)。在恒定负载下运行应用程序24小时并进行堆转储后,我观察到内存使用量不断增加,应用程序在空闲状态下不会释放内存。我知道这将导致java堆内存不足问题

JobsController是一个ejb无状态bean,它的资源在每次调用后都会被正确销毁。 JMSMessageListener由ejb全局bean池处理,其实例被重用

我从java堆转储中看到的可疑对象是

  • EJB bean注入导致内存泄漏
  • ActiveMQConnection.finalize()。如果它是罪魁祸首,那么它必须 所有这些wildfly activemq部署都会发生这种情况。任何暗示都是错误的 谢谢
  • ActiveMQConnection.java

    @Override  
    protected final void finalize() throws Throwable {  
       if (!closed) {  
           if (this.factoryReference.isFinalizeChecks()) {  
               ActiveMQJMSClientLogger.LOGGER.connectionLeftOpen(creationStack);  
           }  
           close();  
    }
    
    @MessageDriven(name = "JMSMessageListener", mappedName = JAVA_JMS_QUEUE, activationConfig = {  
            @ActivationConfigProperty(  
                    propertyName = "acknowledgeMode",  
                    propertyValue = "Auto-acknowledge"),  
            @ActivationConfigProperty(  
                    propertyName = "destinationType",  
                    propertyValue = "javax.jms.Queue"),  
            @ActivationConfigProperty(  
                    propertyName = "destination",  
                    propertyValue = JAVA_JMS_QUEUE)  
    
    
    })  
    public class JMSMessageListener implements MessageListener {  
    
        private static Logger LOG = LoggerFactory.getLogger(JMSMessageListener.class);  
    
        @EJB  
        private JobsController jobsController;  
    
        private final ObjectMapper progressMessageMapper;  
    
        public JMSMessageListener() {  
            progressMessageMapper = new ObjectMapper();  
            progressMessageMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);  
        }  
    
        @Override  
        public void onMessage(Message message) {  
            ProgressMessage progressMessage = null;  
            try {  
                if (message instanceof BytesMessage) {  
                    BytesMessage bytesMessage = (BytesMessage) message;  
                    int TEXT_LENGTH = new Long(bytesMessage.getBodyLength()).intValue();  
                    byte[] textBytes = new byte[TEXT_LENGTH];  
                    bytesMessage.readBytes(textBytes, TEXT_LENGTH);  
    
    
                    String progressText = new String(textBytes, "UTF-8");  
    
                    progressText = progressText.replaceAll("'totalSteps': None", "'totalSteps': 0");  
                    progressMessage = progressMessageMapper.readValue(progressText, ProgressMessage.class);  
                } else if (message instanceof ObjectMessage) {  
                    progressMessage = message.getBody(ProgressMessage.class);  
                }  
                if (progressMessage != null) {  
    
                    jobsController.sendProgressMessage(progressMessage);  
                } else {  
                    LOG.error("An empty progress message was received");  
                }  
            } catch (JMSException | IOException e) {  
                LOG.error("failed to process progress message: {}", e.getMessage(), e);  
            }  
        }  
    }
    

    作业控制器

    @无状态的
    公共类作业控制器{

    @Inject  
    private JMSContext jmsContext;  
    private Connection connection;  
    private Session session;  
    private MessageProducer jmsProducer;  
    
    @Resource(lookup = "java:/ConnectionFactory")  
    private ConnectionFactory connectionFactory;  
    
    @Resource(lookup = JAVA_JMS_JOB_QUEUE)  
    private Queue jobQueue;  
    
    @Resource(lookup = JAVA_JMS_QUEUE)  
    private Queue progressQueue;  
    
    @PreDestroy  
    void release() {  
        try {  
            if (jmsProducer != null) {  
                jmsProducer.close();  
            }  
            if (session != null) {  
                session.close();  
            }  
            if (jmsContext != null) {  
                jmsContext.close();  
            }  
            if (connection !=null) {  
                connection.close();  
            }  
        } catch (JMSException e) {  
            LOG.warn("failed to close JMS resources: {}", e.getMessage());  
        }  
    }  
    
    public synchronized MessageProducer getJmsProducer() {  
        if (jmsProducer == null) {  
            try {  
                connection = connectionFactory.createConnection();  
                session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);  
                jmsProducer = session.createProducer(jobQueue);  
    
    
                connection.start();  
            } catch (JMSException e) {  
                LOG.error("failed to setup JMS message producer: {}", e.getMessage());  
            }  
        }  
        return jmsProducer;  
    }
    public void addMessageToProgressQueue(ProgressMessage progressMessage) {
        ObjectMessage objectMessage = jmsContext.createObjectMessage(progressMessage);
        try {
            getJmsProducer().send(progressQueue, objectMessage);
        } catch (JMSException e) {
            LOG.error("failed to send progress message {}: {}", objectMessage, e.getMessage());
        }
    }  
    
    }

    JMSMessageListener.java

    @Override  
    protected final void finalize() throws Throwable {  
       if (!closed) {  
           if (this.factoryReference.isFinalizeChecks()) {  
               ActiveMQJMSClientLogger.LOGGER.connectionLeftOpen(creationStack);  
           }  
           close();  
    }
    
    @MessageDriven(name = "JMSMessageListener", mappedName = JAVA_JMS_QUEUE, activationConfig = {  
            @ActivationConfigProperty(  
                    propertyName = "acknowledgeMode",  
                    propertyValue = "Auto-acknowledge"),  
            @ActivationConfigProperty(  
                    propertyName = "destinationType",  
                    propertyValue = "javax.jms.Queue"),  
            @ActivationConfigProperty(  
                    propertyName = "destination",  
                    propertyValue = JAVA_JMS_QUEUE)  
    
    
    })  
    public class JMSMessageListener implements MessageListener {  
    
        private static Logger LOG = LoggerFactory.getLogger(JMSMessageListener.class);  
    
        @EJB  
        private JobsController jobsController;  
    
        private final ObjectMapper progressMessageMapper;  
    
        public JMSMessageListener() {  
            progressMessageMapper = new ObjectMapper();  
            progressMessageMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);  
        }  
    
        @Override  
        public void onMessage(Message message) {  
            ProgressMessage progressMessage = null;  
            try {  
                if (message instanceof BytesMessage) {  
                    BytesMessage bytesMessage = (BytesMessage) message;  
                    int TEXT_LENGTH = new Long(bytesMessage.getBodyLength()).intValue();  
                    byte[] textBytes = new byte[TEXT_LENGTH];  
                    bytesMessage.readBytes(textBytes, TEXT_LENGTH);  
    
    
                    String progressText = new String(textBytes, "UTF-8");  
    
                    progressText = progressText.replaceAll("'totalSteps': None", "'totalSteps': 0");  
                    progressMessage = progressMessageMapper.readValue(progressText, ProgressMessage.class);  
                } else if (message instanceof ObjectMessage) {  
                    progressMessage = message.getBody(ProgressMessage.class);  
                }  
                if (progressMessage != null) {  
    
                    jobsController.sendProgressMessage(progressMessage);  
                } else {  
                    LOG.error("An empty progress message was received");  
                }  
            } catch (JMSException | IOException e) {  
                LOG.error("failed to process progress message: {}", e.getMessage(), e);  
            }  
        }  
    }
    
    两件事:

    • 您正在注入JMSContext,但从未使用过它(至少在粘贴的代码中)。这似乎是一个错误
    • 如果您不打算使用注入的JMSContext,而是使用注入的ConnectionFactory,那么您应该注入“java:/JmsXA”而不是“java:/ConnectionFactory”,因为它是一个&tl;池连接工厂>。为使用“java:/ConnectionFactory”发送的每条消息创建连接是一种反模式,因为它不是池式的。此外,我假设您希望使用XA事务,以便MDB中的消息消费和发送是原子的,而这不适用于“java:/ConnectionFactory”

    你好,贾斯汀,谢谢你的建议。我正在尝试您的建议,使用“java:/JmsXA”而不是“java:/ConnectionFactory”,并运行一些压力测试,看看它是否仍然泄漏。我们确实使用JMSContext(将其添加到上面的类JobsController中)。