Java Springboot Maven项目从不运行垃圾收集

Java Springboot Maven项目从不运行垃圾收集,java,spring-boot,garbage-collection,heap-memory,Java,Spring Boot,Garbage Collection,Heap Memory,我有一个Springboot Maven项目,它使用@JmsListener从队列中读取消息 如果没有事件进入,堆内存将缓慢增加。当消息到达时,堆内存会快速增加。但是堆内存永远不会下降(查看下图) 如果我在receiver方法的末尾添加System.gc(),垃圾收集器将按预期完成其工作。但这绝对不是好的做法 如何确保gc在适当的时间运行。任何帮助都将不胜感激 堆内存使用情况 接收器方法 @JmsListener(destination = "${someDestination}", cont

我有一个Springboot Maven项目,它使用@JmsListener从队列中读取消息

如果没有事件进入,堆内存将缓慢增加。当消息到达时,堆内存会快速增加。但是堆内存永远不会下降(查看下图)

如果我在receiver方法的末尾添加System.gc(),垃圾收集器将按预期完成其工作。但这绝对不是好的做法

如何确保gc在适当的时间运行。任何帮助都将不胜感激

堆内存使用情况

接收器方法

@JmsListener(destination = "${someDestination}", containerFactory = "jmsListenerContainerFactory")
    public void receiveMessage(Message message){
        if (message instanceof BytesMessage) {
            try {
                List<Trackable> myList;

                BytesMessage byteMessage = (BytesMessage) message;
                byte[] byteData = new byte[(int) byteMessage.getBodyLength()];
                byteMessage.readBytes(byteData);

                DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                Document doc = dBuilder.parse(new InputSource(new StringReader(new String(byteData))));

                TransformerFactory factory = TransformerFactory.newInstance();
                factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
                Transformer transformer = factory.newTransformer();

                StringWriter writer = new StringWriter();
                transformer.transform(new DOMSource(doc.getElementsByTagName(SOME_TAG_NAME).item(0)), new StreamResult(writer));
                String outputXmlString = writer.getBuffer().toString();

                XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
                XMLStreamReader xmlReader = xmlFactory.createXMLStreamReader(new StringReader(outputXmlString));

                JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);

                MyEvent myEvent = ((JAXBElement<MyEvent>) jaxbContext.createUnmarshaller().unmarshal(xmlReader)).getValue();
                myList = myService.saveEvent(myEvent);

                LOGGER.info(String.format("Received message with EventID: %s and successfully inserted into database", myEvent.getID()));

            } catch (Exception e) {
                LOGGER.error(e.getClass().getCanonicalName() + " in Receiver: ", e);
            }
        } else {
            LOGGER.error("Received unsupported message format from MQ");
        }
    }
@JmsListener(destination=“${someDestination}”,containerFactory=“jmsListenerContainerFactory”)
公共无效接收消息(消息消息){
if(字节消息的消息实例){
试一试{
列出我的清单;
BytesMessage byteMessage=(BytesMessage)消息;
byte[]byteData=新字节[(int)byteMessage.getBodyLength()];
读取字节(byteData);
DocumentBuilder dBuilder=DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc=dBuilder.parse(新的InputSource(新的StringReader(新的字符串(byteData)));
TransformerFactory=TransformerFactory.newInstance();
setFeature(xmlstants.FEATURE\u SECURE\u PROCESSING,true);
变压器=工厂新变压器();
StringWriter编写器=新的StringWriter();
transformer.transform(新的DOMSource(doc.getElementsByTagName(SOME_TAG_NAME).item(0)),新的StreamResult(writer));
String outputXmlString=writer.getBuffer().toString();
XMLInputFactory xmlFactory=XMLInputFactory.newInstance();
XMLStreamReader=xmlFactory.createXMLStreamReader(新StringReader(outputXmlString));
JAXBContext JAXBContext=JAXBContext.newInstance(ObjectFactory.class);
MyEvent MyEvent=((JAXBElement)jaxbContext.createUnmarshaller().unmarshal(xmlReader)).getValue();
myList=myService.saveEvent(myEvent);
LOGGER.info(String.format(“接收到事件ID为:%s并成功插入数据库的消息”,myEvent.getID());
}捕获(例外e){
LOGGER.error(e.getClass().getCanonicalName()+“在接收器:”,e中);
}
}否则{
LOGGER.error(“从MQ接收到不支持的消息格式”);
}
}

为什么?因为
JVM
决定(基于其启发式)现在还不是运行的时候。何时运行取决于堆大小和GC算法。一般来说,运行GC循环绝不是免费的操作——它至少需要GC循环+停止应用程序一段时间(称为
停止世界
事件)。因此,GC算法在需要时运行

当您使用并发收集器时(例如
ZGC
Shenandoah
),它们是否运行并不重要;这是因为它们是并发的:它们在应用程序运行时运行。它们确实有
stop the world
暂停-但这些暂停非常小(例如在某些情况下,与
G1GC
不同)。由于这种并发性,可以强制它们“每X秒”运行一次
Shenandoah
具有
-XX:ShenandoahgGuarantedgCinterval=10000
(我们在生产中使用它)

但是我假设您使用的是
G1GC
(也就是说,如果您根本不启用GC,您就会得到这个结果)。这种特殊的GC主要是并发的,并且是分代的。它将堆分为新区域和旧区域,并独立收集它们。年轻区域是在
STW
暂停下收集的,而
Full GC
(收集旧区域)大部分是并发的:它可以将
STW
暂停延长到分钟,但这不是一般情况

因此,当您使用
G1GC
时,当所有年轻的伊甸园区域(年轻区域在伊甸园中分割,幸存者进一步)都满时,将触发年轻的GC循环。发生以下三种情况之一时,将触发完整GC循环:

 1) IHOP is reached 
 2) G1ReservePercent is reached 
 3) a humongous allocation happens (an allocation that spans across multiple regions - think huge Objects). 
但这是一个相当简单且不完整的关于
G1GC
何时发生
GC循环的图片,主要是因为这三者中的任何一个都会触发一个
标记
阶段(整个GC的某一部分),该阶段将根据从各个区域收集的数据决定下一步要做什么。它通常会立即触发一个年轻的GC,然后触发一个
混合集合
,但可能会选择不同的路径(同样,基于GC拥有的数据)


因此,总体而言,堆上的压力太小,无法启动
GC循环,而且大多数情况下,这正是您想要的

以惊人的40MB“快速提升”…您的堆大小为~4GB,并且想知道为什么JVM不浪费CPU周期来回收小于175MB(0.175GB)的内存?好了,所以GC在内存达到一定限制之前不会运行。我假设它也会在应用程序空闲时运行。这取决于配置的GC算法。并发垃圾收集器不会等到内存已满,但它们的阈值通常远远大于配置的堆大小的5%。如果175MB确实是一个问题,那么您必须手动配置阈值或减小最大堆大小。否则,您已经证明显式GC可以回收内存,因此没有泄漏,您可以让JVM填充堆内存并在需要时运行GC。感谢您的详细解释。