Since upgrading from 4.1.3 (running perfectly stable for months on Raspi 4 / Docker) to 4.2 I‘m experiencing freezes every 12ish hours. The logs mention a lack of memory:
*java.lang.OutOfMemoryError: Java heap space*
*2024-07-13 13:36:33.567 [ERROR] [org.apache.felix.fileinstall ] - In main loop, we have serious trouble*
*java.lang.OutOfMemoryError: Java heap space*
*2024-07-13 13:36:34.433 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'strom_split_stromzaehler': Failed to execute action: 2(Java heap space)*
Anyone else experiencing similar freezes? Since 4.2 a rule throws errors (still have to debug), but a rule making OH crash completely sounds unlikely.
That indicates java crashing because it needs to use more RAM than you gave in your setup.
That might have been triggered by 4.2 install overwriting startup parameters or some such, and eventually 4.2 uses some more RAM, but in the end that’s not an openHAB issue.
You need to fix your Docker/Java setup.
Marketplace add-ons might have similar issues as jar/kar files in the add-ons folder if the developers are not careful with the versioning in the title and keeping up with changes in core.
I’m somewhat puzzled. My docker configuration is hosting 10+ containers, and openHAB has been running smoothly for 3+ years, having gone through multiple updates without any stability problems. And then, directly after updating to 4.2, it feezes due to out-of-memory problems.
I stumbled upon this post here. Is there a chance that maybe something in 4.2 causes a memory leak like in this older post?
Update: At least judging from the systeminfo-binding, 4.2 does not seem to consume more RAM (left: 4.1.3, right: 4.2 with three freezes & reboots)..
I‘ve investigated the freeze-problem, which was caused by RAM-issue, which occurred on my installation after updating to 4.2:
java.lang.OutOfMemoryError: Java heap space
2024-07-13 13:36:33.567 [ERROR] [org.apache.felix.fileinstall] - In main loop, we have serious trouble
java.lang.OutOfMemoryError: Java heap space
2024-07-13 13:36:34.433 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'strom_split_stromzaehler': Failed to execute action: 2(Java heap space)
Below is the RAM graph of the systeminfo-binding of these instances:
After the update to 4.2, two step changes in RAM, followed by a freeze for 6+ hours
Restart of the container. Then the same two-step change in RAM, followed by a freeze
Restart of the container. Then the same two-step change in RAM, followed by a freeze
I then deactivated a single rule (see below), since then normal operations.
I changed two things, before things seem to have settled in #5: I purged all orphans (though this should not have caused the instability, since they’ve been there all along but not visible via the nice new feature), and deactivated a single rule that threw an error only after updating to 4.2:
2024-07-14 23:41:12.057 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'strom_split_stromzaehler' failed: cannot invoke method public default long java.time.chrono.ChronoZonedDateTime.toEpochSecond() on null
In case someone wants to investigate (is it possible that the “long” operations in the rule caused the RAM issue and ultimately the freeze?), let me know.
long is just the return type of the toEpochSecond() method. The reason for the error is that the method is being called on a variable that’s null.
Can you provide more info on the rule? The rule language, actual code, how often it is run etc. If it’s run very frequently and handles a lot of data, this could be left orphaned but still allocated when the rule crashes, leading to increasing memory use.
I am almost embarrased to post the rule here, as it’s one of my legacy rules that’s run every time my electricity meter updates (so probably x00 times per day), to split between “energy consumed” and “energy fed back into the grid”. It’s one of my old DSL-rules, and I should now probably refactor it if I find the time.
logWarn("notifications", "Regel zum Berechnen des Netzbezugs & Netzeinspeisung")
// Nur ausführen alls die Regel durch ein Update des Stromzählers getriggert wurde
if (triggeringItemName == "Stromzaehler_Electricmeterwatts"){
//logWarn("notifications", "Item-Trigger")
// Zählerstände ausrechnen
var Number PreviousValueInWatt = Stromzaehler_Electricmeterwatts.previousState(true).state as Number
//logWarn("notifications", "Leistung vorletztes Update: " + PreviousValueInWatt + " Watt")
var Number LastValueInWatt = Stromzaehler_Electricmeterwatts.state as Number
//logWarn("notifications", "Leistung letztes Update: " + LastValueInWatt + " Watt")
//var LastValueInWatt = (Stromzaehler_Electricmeterwatts.state as QuantityType<Number>).toBigDecimal
// Zeiten ausrechnen
// Zeitpunkt der vorletzten Aktualisierung in Variable PreviousUpdateInEpochSeconds speichern
//logWarn("notifications", "Zeitpunkt vorletztes Update: " + Stromzaehler_Electricmeterwatts.previousState(true).timestamp.toString)
var Number PreviousUpdateInEpochSeconds = Stromzaehler_Electricmeterwatts.previousState(true).timestamp.toEpochSecond
//logWarn("notifications", " (oder: " + PreviousUpdateInEpochSeconds.toString + " Epoch-Sekunden)")
// Zeitpunkt der letzten Aktualisierung in Variable LastUpdateInEpochSeconds speichern
//logWarn("notifications", "Zeitpunkt letztes Update: " + Stromzaehler_Electricmeterwatts.lastUpdate)
var Number LastUpdateInEpochSeconds = Stromzaehler_Electricmeterwatts.lastUpdate.toEpochSecond
//logWarn("notifications", " (oder: " + LastUpdateInEpochSeconds.toString + " Epoch-Sekunden)")
// Zeitdifferenz ausrechnen
var UpdateDifferenceInEpochSeconds = LastUpdateInEpochSeconds - PreviousUpdateInEpochSeconds
// Statusmeldung (x Watt über y Sekunden) ausgeben
logWarn("notifications", "Leistung in abgelaufener Periode: " + Stromzaehler_Electricmeterwatts.previousState(true).state + " Watt über " + UpdateDifferenceInEpochSeconds.toString + " Sekunden")
// Falls "Netzbezug" in der abgelaufenen Periode (vorletzter Wert >= 0)
if(Stromzaehler_Electricmeterwatts.previousState(true).state >= 0){
logWarn("notifications", "Fall: Netzbezug (Verbrauch in abgelaufener Periode größer oder gleich 0)")
var Number tempNetzbezug = ((PreviousValueInWatt * UpdateDifferenceInEpochSeconds)/3600000)
logWarn("notifications", "Netzbezug (temp-Variable) seit letzter Stromzähler-Änderung (in kWh): "+ tempNetzbezug)
var Netzbezug = (stromzaehler_netzbezug_kWh.state as QuantityType<Number>).toBigDecimal
logWarn("notifications", "Netzbezug (Variable aus Item gelesen) heute vor Aktualisierung (in kWh): "+ Netzbezug)
Netzbezug = Netzbezug + tempNetzbezug
logWarn("notifications", "Netzbezug (Variable) heute nach Aktualisierung (in kWh): "+ Netzbezug)
stromzaehler_netzbezug_kWh.postUpdate(Netzbezug as Number)
// Falls "Netzeinspeisung" in der abgelaufenen Periode (vorletzter Wert < 0)
} else {
logWarn("notifications", "Fall: Netzeinspeisung (Verbrauch in abgelaufener Periode kleiner 0)")
var Number tempNetzeinspeisung = ((PreviousValueInWatt * UpdateDifferenceInEpochSeconds)/3600000)
logWarn("notifications", "Netzeinspeisung (temp-Variable) seit letzter Stromzähler-Änderung (in kWh): "+ tempNetzeinspeisung)
var Netzeinspeisung = (stromzaehler_netzeinspeisung_kWh.state as QuantityType<Number>).toBigDecimal
logWarn("notifications", "Netzeinspeisung (Variable aus Item gelesen) heute vor Aktualisierung (in kWh): "+ Netzeinspeisung)
Netzeinspeisung = Netzeinspeisung + tempNetzeinspeisung
logWarn("notifications", "Netzeinspeisung (Variable) heute nach Aktualisierung (in kWh): "+ Netzeinspeisung)
stromzaehler_netzeinspeisung_kWh.postUpdate(Netzeinspeisung as Number)
}
}
else {
logWarn("notifications", "Keine Berechnungen durchgeführt")
}
I’m not sure when this change happened, but the lastUpdate() method returns null if the last persisted state is different from the current item state (see the docs), which could happen if the rule is triggered on item change: the new state hasn’t had the time to be persisted yet, so the one read is the old value. But it seems what you are after is the time passed from the previous change to the curren one, right? Then why not just use ZonedDateTime.now().toEpochSecond() instead?