我有一个Springboot Maven项目,该项目使用@JmsListener从队列中读取消息。
如果没有事件进入,堆内存将缓慢增加。消息到来时,堆内存将快速增加。但是堆内存永远不会崩溃(请参见下面的图像)。
如果我在接收方方法的末尾添加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");
}
}
为什么?因为JVM
决定(根据其启发式方法)还不是运行时间。但是,何时运行时间取决于堆大小和GC算法。通常,运行GC周期绝不是免费的操作-至少需要GC周期+停止应用程序一段时间(称为stop-the-world
事件)。因此,GC算法会在需要时运行。
[当您使用并发收集器时(例如ZGC
或Shenandoah
),无论它们是否运行,都没有关系[stop-the-world
暂停-但它们很小(例如在某些情况下不同于G1GC
)。由于这种并发性,它们被迫“每X秒”运行; Shenandoah
具有-XX:ShenandoahGuaranteedGCInterval=10000
(我们在生产中使用它)。但是我假设您使用的是G1GC
(即,如果您根本不启用GC,就会得到此信息)。该特定GC是大多
世代。它将堆拆分为young和old区域,并独立收集它们。年轻区域是在STW
暂停下收集的,而Full GC
(收集旧区域)是主要并发的:从字面上看,它可以将STW
暂停延长到几分钟,但这不是一般情况。 >所以,当您使用G1GC
时,当all年轻的Eden区域(年轻的区域在Eden和Survivor中分开)将触发年轻的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).
但是,这是GC cycle
何时发生G1GC
的一种简单且不完整的图,主要是因为这三个中的任何一个实际上会触发一个mark
阶段(整个Full GC的特定部分),会根据从区域收集的数据来决定下一步要做什么。通常,它会立即触发young GC
then mixed Collection
,但可能会选择其他路径(再次基于GC所具有的数据)。