- Published on
Object Lifecycle & Memory Discipline di Embedded
- Authors
π Artikel 3: Object Lifecycle & Memory Discipline di Embedded
Posisi: Artikel 3 dari 8 Domain Keputusan: Memory & Allocation Policy Status Lock: π Allocation & Lifecycle Freeze Entry Context: IndustrialNode/IndustrialNode.ino
- π Artikel 3: Object Lifecycle & Memory Discipline di Embedded
1. Problem Reality
Setelah Artikel 2:
- Tidak ada global mutable.
- Dependency sudah eksplisit.
- State punya owner.
Firmware terlihat rapi.
Namun setelah 2β4 minggu deployment, gejala mulai muncul:
- Reset tanpa sebab jelas.
- TLS handshake gagal sporadis.
- OTA kadang gagal.
- Heap minimum terus menurun.
- Watchdog reset saat traffic tinggi.
Kode terlihat bersih.
Masalahnya bukan dependency lagi.
Masalahnya:
Lifecycle object dan penggunaan heap tidak disiplin.
Firmware embedded tidak boleh bergantung pada βruntime flexibilityβ seperti sistem desktop.
ESP32 memiliki:
- Heap terbatas
- TLS yang memakan memori besar
- WiFi stack internal
- FreeRTOS task internal
Heap bukan milik kita sepenuhnya.
2. Root Cause Analysis
Masalah memory discipline di firmware Arduino ESP32 biasanya berasal dari 4 pola berikut.
2.1 Dynamic Allocation Tersembunyi
Contoh klasik:
String payload = "{ \"temp\": " + String(temp) + " }";
client.publish("node/data", payload.c_str());
Yang terjadi:
- Allocation heap
- Reallocation saat concat
- Copy internal buffer
Setiap loop publish β heap churn.
Fragmentasi tidak langsung terasa. Tetapi setelah minggu ke-3, TLS gagal.
2.2 Object Dibuat di Dalam Loop
void loop() {
TelemetryPacket pkt;
pkt.build();
comm.publish(pkt);
}
Jika TelemetryPacket memiliki buffer internal besar:
- Stack pressure meningkat.
- Lifecycle implicit.
- Sulit diaudit.
Jika ada allocation di constructor, makin berbahaya.
2.3 Growable Container Tanpa Batas
std::vector<Telemetry> buffer;
buffer.push_back(data);
Tanpa reserve() dan tanpa batas:
- Reallocation berkali-kali
- Fragmentasi
- Latency spike
Embedded β server.
2.4 Destructor Mengandung Logic Berat
~CommManager() {
mqtt.disconnect();
}
Destructor tidak eksplisit dipanggil oleh engineer.
Dalam embedded tanpa exception control yang ketat:
- Destructor berat tidak deterministik.
- Bisa memicu blocking tak terduga.
π Visualisasi Fragmentasi Heap


Fragmentasi bukan berarti heap habis. Masalahnya adalah:
- Free memory terpecah kecil-kecil.
- TLS membutuhkan blok besar kontigu.
- Allocation gagal walau free memory terlihat cukup.
Ini silent killer.
3. Design Principle (Rule yang Dikunci)
Artikel ini mengunci baseline memory final.
π Rule 1 β Static-First Allocation
Semua object utama firmware harus:
- Dideklarasikan statik di
IndustrialNode.ino - Lifetime = lifetime firmware
Contoh:
RelayDriver relay(5);
ActuatorService actuator(relay);
ControlApp app(actuator);
Tidak dibuat di dalam loop().
π Rule 2 β Heap Restricted Zone
Heap hanya boleh digunakan untuk:
- TLS internal
- MQTT internal buffer
- OTA buffer terbatas
Selain itu β dilarang.
π Rule 3 β No Allocation in Control Loop
Dilarang di app.run() atau loop():
newdeleteString- container growable
Control loop harus deterministic.
π Rule 4 β No Allocation in ISR
ISR hanya boleh:
- Set flag
- Notify task
- Push ke queue fixed-size
Tidak boleh allocate.
π Rule 5 β Destructor Harus Trivial
Destructor tidak boleh:
- Blocking
- Publish
- Logging berat
- Free resource kompleks
Lifecycle harus eksplisit.
Setelah Artikel 3:
- β Tidak boleh introduce dynamic allocation runtime
- β Tidak boleh
Stringdi hot path - β Tidak boleh container growable tanpa batas
Memory baseline terkunci.
4. Implementation Pattern (ESP32 Arduino Context)
4.1 Static Composition Root
Di IndustrialNode.ino:
#include "drv_RelayDriver.h"
#include "svc_ActuatorService.h"
#include "app_ControlApp.h"
RelayDriver relay(5);
ActuatorService actuator(relay);
ControlApp app(actuator);
void setup() {
app.init();
}
void loop() {
app.run();
}
Semua object utama:
- Dibuat sekali
- Tidak pernah dihancurkan
- Lifetime eksplisit
4.2 Fixed-Size Buffer Pattern
Daripada:
std::vector<Telemetry> buffer;
Gunakan:
#define TELEMETRY_BUFFER_SIZE 10
Telemetry telemetryBuffer[TELEMETRY_BUFFER_SIZE];
uint8_t head = 0;
uint8_t count = 0;
Bounded. Predictable. Audit-able.
5. Constraint & Embedded Impact
Disiplin memory bukan soal βgaya C++ lamaβ. Ini soal survival di lingkungan dengan batas keras.
5.1 RAM Impact
ESP32 (Arduino core) berbagi RAM untuk:
- WiFi stack
- TCP/IP
- TLS (mbedTLS)
- MQTT client
- FreeRTOS task
- Heap user
Jika firmware menggunakan dynamic allocation liar:
- Heap minimum turun perlahan.
- Fragmentasi meningkat.
- Allocation besar (TLS handshake) gagal.
Static-first allocation:
- Menghilangkan churn heap.
- Mengurangi fragmentasi.
- Membuat penggunaan RAM predictable.
5.2 Flash Impact
Menghindari template berat dan container kompleks:
- Mengurangi instantiation code.
- Mengurangi binary size.
- Mengurangi risiko OTA gagal karena ukuran firmware terlalu besar.
Flash bukan infinite resource.
5.3 Stack Impact
Object besar di stack:
void run() {
LargePacket pkt;
}
Jika LargePacket memiliki buffer 512 byte:
- Stack task bisa overflow.
- Crash sporadis sulit dideteksi.
Solusi:
- Gunakan static buffer.
- Hindari object besar di stack lokal.
5.4 Determinism Impact
Dynamic allocation:
- Waktu eksekusi tidak konstan.
- Reallocation latency tidak terprediksi.
- TLS handshake bisa memicu spike.
Control loop harus:
- Konstan
- Ringan
- Tidak tergantung heap
Memory discipline = determinism.
π Visualisasi Stack vs Heap Risk



Jika stack overflow atau heap terfragmentasi, hasilnya bukan warning β tapi reset.
6. Failure Scenario
Memory problem jarang langsung meledak. Ia perlahan merusak stabilitas.
Scenario 1 β Fragmentasi Heap Mingguan
Kondisi:
- Publish tiap 5 detik.
- Gunakan
String. - TLS aktif.
Minggu 1: Stabil. Minggu 2: Heap minimum turun. Minggu 3: TLS handshake gagal. Minggu 4: Device reset.
Root cause:
- Reallocation berulang.
- Fragmentasi kontigu hilang.
Dengan static-first policy:
- Tidak ada churn.
- Heap stabil.
Scenario 2 β Burst Telemetry Buffer Tanpa Batas
WiFi down 30 menit.
Firmware menggunakan:
std::vector<Telemetry> backlog;
Backlog tumbuh terus.
Saat reconnect:
- Publish burst.
- Allocation besar.
- Crash.
Dengan fixed-size ring buffer:
- Data lama di-drop terkontrol.
- Tidak ada spike allocation.
Scenario 3 β Object Besar di Stack
Callback parsing JSON membuat object lokal besar.
Stack default task tidak cukup.
Hasil:
- Crash sporadis.
- Sulit reproduksi.
Dengan static buffer:
- Stack stabil.
- Predictable.
Scenario 4 β Destructor Logic Berat
Object lokal memiliki destructor yang memanggil disconnect().
Ketika keluar scope:
- Blocking terjadi.
- Latency spike.
- Watchdog reset.
Destructor harus trivial.
7. Anti-Pattern
Daftar merah Artikel 3:
- β
new/deletedi runtime loop - β
Stringdi hot path - β
std::vectortanpa batas - β Object besar di stack
- β Allocation di ISR
- β Destructor memanggil network
- β Library pihak ketiga tanpa audit allocation
Dampak:
- Fragmentasi
- Latency spike
- Reset misterius
- OTA gagal
8. Freeze Point
Setelah Artikel 3, keputusan berikut dianggap final:
- Static-first allocation policy.
- Heap hanya untuk zona terbatas (TLS/MQTT/OTA).
- Tidak ada dynamic allocation di control loop.
- Tidak ada
Stringdi path kritikal. - Tidak ada allocation di ISR.
- Destructor harus trivial.
Memory baseline tidak boleh berubah di artikel berikutnya.
9. Engineering Checklist
Audit sebelum release:
- Apakah ada
newataudelete? - Apakah ada
Stringdi loop? - Apakah ada container growable tanpa batas?
- Apakah heap minimum dimonitor?
- Apakah ada object besar di stack?
- Apakah ISR allocate?
Jika satu saja βyaβ β melanggar Artikel 3.
10. Summary (5 Bullet Maksimal)
- Fragmentasi heap adalah silent killer firmware.
- Static-first allocation membuat sistem predictable.
- Dynamic allocation merusak determinism.
- Destructor berat tidak cocok untuk embedded.
- Memory discipline menentukan uptime jangka panjang.
Catatan Penyusunan Artikel ini disusun sebagai materi edukasi dan referensi umum berdasarkan berbagai sumber pustaka, praktik lapangan, serta bantuan alat penulisan. Pembaca disarankan untuk melakukan verifikasi lanjutan dan penyesuaian sesuai dengan kondisi serta kebutuhan masing-masing sistem.