Nalaz 25. septembra 2026. Pitanje je bilo: „ne mogu da stavim breakpoint na atribut?" — može se, i to bez getera i setera.
Kompajler uvek emituje <init> u bytecode, i kad ga nisi napisao. Zato <init> ima svoju Location i može da nosi breakpoint. Dokaz iz našeg traga (anonimna klasa, konstruktor nigde nije napisan):
io.cortiveo.sample.LegacyApp$Db$1.<init>() LegacyApp.java:193U Cortiveo tracerom:
cortiveo trace --app db.,gjava. --call db.tehnobiro.generator3.DataHPrint.<init> --args 3Dve napomene:
super(), pa se u tragu vidi više <init> okvira za jedan objekat.Unsafe.allocateInstance, clone(). Tada <init> ne postoji kao događaj. Za te slučajeve JDI ume da prebroji žive instance (VirtualMachine.instanceCounts(...), ReferenceType.instances(max)) — pitanje se menja iz „ko ga je napravio" u „koliko ih ima i ko ih drži".Ovo je pravi odgovor na zaobilazni put sa geterima/seterima. JDI ima watchpoint:
erm.createModificationWatchpointRequest(Field) // UPIS
erm.createAccessWatchpointRequest(Field) // CITANJEDogađaj (WatchpointEvent) nosi sve što treba:
| metoda | šta daje |
|---|---|
valueCurrent() | stara vrednost |
valueToBe() | nova vrednost (samo kod upisa) |
object() | koja instanca (null za static) |
location() | fajl:linija koda koji je to uradio |
thread() | nit |
U Cortiveo tracer je to ugrađeno (fddecc0):
cortiveo trace --app io.cortiveo. --field "io.cortiveo.sample.LegacyApp$InvoicePanel.*"SET LegacyApp$InvoicePanel.confirm: null -> SpecialInvoiceButton@1205 na InvoicePanel@1204
<- io.cortiveo.sample.LegacyApp$InvoicePanel.<init> LegacyApp.java:262
SET LegacyApp$InvoicePanel.table: null -> JTable@1302 na InvoicePanel@1204
<- io.cortiveo.sample.LegacyApp$InvoicePanel.<init> LegacyApp.java:263Oba ta upisa su direktna — inicijalizatori polja, nijedan seter. Klasa.* prati sva polja klase, --fieldread prati čitanja.
Zašto je to bolje od getera/setera: JVM javlja pristup na nivou polja, pa se ne može promašiti ni jedno mesto. Tvoja zamerka na zaobilazni put je bila tačna: ako jedno direktno čitanje ostane, informacija se tiho gubi — a watchpoint nema tu rupu.
Cena: kao breakpoint — plaća se samo kad se polje dirne. Ali polje u vrućoj petlji košta kao MethodEntry, pa se ne stavlja na brojače i kursore.
Ograničenja: watchpoint je po polju, ne po objektu — pale se svi objekti te klase (filtriraj po object().uniqueID()). Provera podrške: canWatchFieldModification() / canWatchFieldAccess() (HotSpot podržava oba).
Ne nadaj se da nisi promašio mesto, dokaži: bytecode nosi instrukcije putfield/getfield, pa
javap -c -p -cp app.jar db.tehnobiro.Obrazac | grep -E "(put|get)field.*Polje"nađe svaki direktan pristup, uključujući onaj u klasama koje si zaboravio.
Ovo je izvorni dom pojma:
(gdb) watch mojaPromenljiva # prekid na UPIS
(gdb) rwatch mojaPromenljiva # na CITANJE
(gdb) awatch mojaPromenljiva # na oba
(lldb) watchpoint set variable mojaPromenljivaVisual Studio isto ima Data Breakpoint. Mehanizam su debug registri procesora (x86: DR0–DR3), odakle i glavno ograničenje: najviše 4 watchpointa, svaki na 1/2/4/8 bajtova. Kad se pređe, debager pada na software watchpoint = izvršavanje korak-po-korak, sto puta sporije.
Druga zamka: watchpoint je na adresi. Ako se memorija oslobodi i realocira, watchpoint gleda u tuđe podatke. Za „ko je pisao po ovoj memoriji" tu su i AddressSanitizer, Valgrind/memcheck i rr (snimi izvršavanje, pa reverse-continue do upisa).
INotifyPropertyChanged.ba w4 <adresa> radi na adresi, ali GC pomera objekte, pa važi samo kratko (ili uz pin).Java je tu najbolje opremljena: watchpoint je na polju, ne na adresi, pa ga GC ne kvari, i nema ograničenja od 4 kao hardverski registri. Ono što u C/C++ mora u hardver, JVM daje kroz JDWP.
| pitanje | alat |
|---|---|
| ko kreira objekat | --call Klasa.<init> (radi i za podrazumevani konstruktor) |
| koliko ih ima / ko ih drži | instanceCounts, ReferenceType.instances |
| ko menja polje | --field Klasa.polje |
| ko čita polje | --fieldread Klasa.polje |
| ko poziva metodu | --call Klasa.metoda + lanac pozivalaca (--stack N) |
| šta je klik uradio | --trigger actionPerformed + --args N |
| gde ode vreme | --progress + === end click brojevi |
Sve to važi i za kod bez izvora — dovoljan je bytecode sa debug informacijama (GTehnobiro ga ima: 47/47 listenera dobija fajl:linija).