2017-08-31 115 views
0

我們有一個java web應用程序,它通過JMS發送(JobsController.java)並接收消息(JMSMessageListener.java)。恆定負載下運行的應用程序24小時,採取堆轉儲後,我觀察內存使用量的不斷增加,應用程序不放手,當處於空閒狀態。我知道這會導致java堆內存不足的問題。Wildfly 10.1.0.FINAL上的內存泄漏(java.lang.ref.Finalizer/ActiveMQConnection)

JobsController是一個EJB無狀態Bean和它的資源每次通話後正確銷燬。 JMSMessageListener得到由EJB全球Bean池處理,它的實例被重用。

我可以從Java堆轉儲看到的嫌疑人

  1. EJB豆注射引起內存泄漏 https://blog.akquinet.de/2017/01/04/dont-get-trapped-into-a-memory-leak-using-cdi-instance-injection/
  2. ActiveMQConnection.finalize()。如果它比它必須 發生在所有這些wildfly的ActiveMQ部署的罪魁禍首。任何暗示是 讚賞。

ActiveMQConnection.java

@Override 
protected final void finalize() throws Throwable { 
    if (!closed) { 
     if (this.factoryReference.isFinalizeChecks()) { 
      ActiveMQJMSClientLogger.LOGGER.connectionLeftOpen(creationStack); 
     } 
     close(); 
} 

enter image description here enter image description here enter image description here

JobsController

@Stateless
公共類JobsController {

@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

@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); 
     } 
    } 
} 

回答

1

幾件事情:

  • 你注入JMSContext但從來沒有使用它(至少在你粘貼的代碼中)。這似乎是一個錯誤。
  • 如果您不打算使用注入的JMSContext,而是使用注入的ConnectionFactory,那麼您應該注入「java:/ JmsXA」而不是「java:/ ConnectionFactory」,因爲它是&tl; pooled-connection-factory> 。爲使用「java:/ ConnectionFactory」發送的每條消息創建一個連接是一種反模式,因爲它沒有被合併。另外,我想你想使用一個XA事務,這樣的消息消費和你的MDB發送是原子,並且不會「的java:/ ConnectionFactory的」工作。
+0

嗨@Justin,謝謝您的建議。我正在嘗試使用「java:/ JmsXA」而不是「java:/ ConnectionFactory」的建議,並且正在運行一些壓力測試,以查看它是否仍然泄漏。我們使用JMSContext(將其添加到類JobsController中)。 – tchoesang