Nie*_*son 5 java garbage-collection heap-memory spring-boot
我有一个 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");
}
}
Run Code Online (Sandbox Code Playgroud)
为什么?因为JVM(根据其启发式)决定现在还不是运行的时间。何时运行取决于堆大小和 GC 算法。一般来说,运行 GC 周期绝不是一个免费的操作 - 它stop-the-world至少需要 GC 周期 + 停止应用程序一段时间(称为事件)。因此,GC 算法会在需要时运行。
当您使用并发收集器(ZGC或Shenandoah例如)时,它们是否运行并不重要;这是因为它们是并发的:它们在您的应用程序运行时运行。他们确实有stop-the-world停顿 - 但这些停顿非常小(G1GC与某些情况不同)。由于这种并发性,它们可能被迫“每 X 秒”运行一次;Shenandoah有-XX:ShenandoahGuaranteedGCInterval=10000(我们在生产中使用它)。
但我假设您正在使用G1GC(即如果您根本不启用 GC,这就是您得到的结果)。这个特定的 GC大部分是并发的并且是分代的。它将堆分成新区和旧区并独立收集它们。年轻区域是在暂停下收集的STW,而 a Full GC(收集旧区域)主要是并发的:从字面上看,它可以将暂停延长STW到几分钟,但这不是一般情况。
因此,当您使用 时,当所有年轻 Eden 区域(年轻区域在 Eden 和 Survivor 中进一步分裂)已满G1GC时,将会触发年轻 GC 周期。当以下 3 种情况之一发生时,将触发 Full GC 周期:
1) IHOP is reached
2) G1ReservePercent is reached
3) a humongous allocation happens (an allocation that spans across multiple regions - think huge Objects).
Run Code Online (Sandbox Code Playgroud)
但这是一个相当简单且不完整的关于 aGC cycle发生时的情况G1GC,主要是因为这 3 个中的任何一个实际上都会触发一个mark阶段(整个 Full GC 的某个部分),该阶段将根据它的数据决定下一步要做什么从地区聚集。它通常会立即触发年轻的 GC ,然后mixed Collection选择不同的路径(同样,基于 GC 拥有的数据)。
所以总的来说,你对堆的压力太小而无法GC cycle启动,而且大多数时候 - 这正是你想要的。
| 归档时间: |
|
| 查看次数: |
1037 次 |
| 最近记录: |