From 836868d6858ac0b944c1d55d9e6f044b6e120efa Mon Sep 17 00:00:00 2001 From: Sushrut Mishra Date: Thu, 9 Oct 2025 10:25:35 +0530 Subject: [PATCH] demo: add NullPointerException example and NpeRunner --- out/LargeLeakyService$CacheManager.class | Bin 0 -> 1069 bytes out/LargeLeakyService$CachedValue.class | Bin 0 -> 391 bytes out/LargeLeakyService$EventBus.class | Bin 0 -> 1563 bytes out/LargeLeakyService$EventListener.class | Bin 0 -> 278 bytes out/LargeLeakyService$MetricsCollector.class | Bin 0 -> 1341 bytes out/LargeLeakyService$RequestContext.class | Bin 0 -> 423 bytes out/LargeLeakyService$RequestHandler$1.class | Bin 0 -> 1140 bytes out/LargeLeakyService$RequestHandler.class | Bin 0 -> 1733 bytes out/LargeLeakyService.class | Bin 0 -> 3432 bytes out/Main.class | Bin 0 -> 440 bytes src/LargeLeakyService.java | 206 +++++++++++++++++++ src/NpeRunner.java | 5 + 12 files changed, 211 insertions(+) create mode 100644 out/LargeLeakyService$CacheManager.class create mode 100644 out/LargeLeakyService$CachedValue.class create mode 100644 out/LargeLeakyService$EventBus.class create mode 100644 out/LargeLeakyService$EventListener.class create mode 100644 out/LargeLeakyService$MetricsCollector.class create mode 100644 out/LargeLeakyService$RequestContext.class create mode 100644 out/LargeLeakyService$RequestHandler$1.class create mode 100644 out/LargeLeakyService$RequestHandler.class create mode 100644 out/LargeLeakyService.class create mode 100644 out/Main.class create mode 100644 src/LargeLeakyService.java create mode 100644 src/NpeRunner.java diff --git a/out/LargeLeakyService$CacheManager.class b/out/LargeLeakyService$CacheManager.class new file mode 100644 index 0000000000000000000000000000000000000000..1907e76dc6d80565a0adfd75dc15f3a25460eb8c GIT binary patch literal 1069 zcma)5O>fgc5Pch`aqT!w8cHdJ7WzSm)6jt6fRw5SB2X)(#qY@lJ z0*M0$_!}HR0#yMCapp%MX59$#Md7lu`*z-&+41v_pI^QKxQ|;p5>Qkm4NM@#kUQkZ zyy9}tt~_iV3M*ttZ8)A2ZZarEv#DZ|p%5oUq2pGpwr54YFTAi)`@P-b!NCsi=un|( zNW)N(F))QJ!(yHLwx|pK8@TtfV?=WQRe@c*2HH~THRh%<0 zgYyiTfmBA(;6gqI8~%b8DDuHf`^t>M@n4==q zwe~(~W$TV7cB7+~@E`G(OURx6=l!3a*<`pqhGzbw4+g!_j(kfzaAf7#k*?R}w`7>y z6+yVw4nh?-7^X+k7>sSt6MoI*K_CJ;4*Dh?A5UkFB{E%t{LPS;wxhU0rvz5XA84B%yTEvXPte!OUE~xz z9;Us7fmcMBL$OCIken2pifLWUCh#5e9|$4O(JC7QUeij3>2a1RN>rDeE-o{?AIg72 s_vEBreqk{GD#LJJZ=vBGwQrJ60wt`)v5a+!c|y`OmekR;m}Nc-Fg{%sI#irOPl5vnxWO z-5FajVHvO;l;IGLeU;4EXZ83z;$#&DeAZXN15d{)S~5Wkrv&oyu8M;yY9eK+NCQH# z?F~9(g5FH@7J)CgmiqU|pgM6rc-NyPu$LYYn4&vc}HG mrf^rVzDd6bzQV#ADtMQcJ;`X;M`OcH917bq(nJ$Su>Jsms!LY@ literal 0 HcmV?d00001 diff --git a/out/LargeLeakyService$EventBus.class b/out/LargeLeakyService$EventBus.class new file mode 100644 index 0000000000000000000000000000000000000000..6668ff9798abb409821d55b82cd88dc6ed998bf9 GIT binary patch literal 1563 zcmah}ZF3V<6n<{rZeF&~7pSBlg<5IS0;wPfNvS|6QVl^!rw)$JxJ~Y)+orp9cM~c9 zgkR7veqlzZ1ZLFHZ~g{j{IELc_ zVl^rj7lrJ%8vCJv(;66a)rQkjy7M!dzOw@9{x*6&7qZJ5_>qB0d@K;ReC3+H;|h#s z`ukk$*b6C4AtRABFpU|3;a-Vd7Xi6ydS&&5GjyjXZrRhxSBnuF^G z@+h!IkHZg2U*xzdR^2xJrc2A^L*tq&y{Fj1ybf1%Y6KrGS+*)SH&&FpWUe%5iMy(< zyCT<w>M zPyN*ZzC#4x)A=TDb-)?g^&4aQ7Yxp{F&e@*hfzQ9BVp42_B&Ch{s&bfN)c7Wl}@hL zEL1YrBc1DUKJuroRqHz#FQ+4aZ6lSBQl_K7Uua`OARkFb+Bhfh3}>`VR(kD7| zzKu&^Y$KYFZSVc_oRQ<))iFjN!+0QXVi6@=Wp}#&b4c*HMz3{}*}w!g$bv(kAL+G; zGF&`_hdcCILTryAq7p+A3z8A^Pe^>RM=kQ7Su!JDLrP5Taaytd7qm&UKqc15#_=@#uc^?lbhp7!w|C;^GQa+3&{sqwN BU2XsX literal 0 HcmV?d00001 diff --git a/out/LargeLeakyService$EventListener.class b/out/LargeLeakyService$EventListener.class new file mode 100644 index 0000000000000000000000000000000000000000..cf9afca06b8a4d56bfd225320686fadaf10c9ff8 GIT binary patch literal 278 zcmZXPJqp4=5QX3PlQr=NZES4>wb27uilPv-2-vS-2#H|>yKBI!S$F^sC9Ybi*vuPd z-pqV+yI-yVmgopn2!l}SghQ6cbHut#V;(L`RyItHWyM;cM(Ad;l(Sr_WVSnG99x1Y z)XPU0jl)knvN~1C{HyXFm{0bEASzBe=2e=6i6_aQ(Iy$;W1RR|X=XuyAIfR@w1IUN<+p8o(oEJz{% literal 0 HcmV?d00001 diff --git a/out/LargeLeakyService$MetricsCollector.class b/out/LargeLeakyService$MetricsCollector.class new file mode 100644 index 0000000000000000000000000000000000000000..504e3a3887b13488e85d096d251ceb206abea269 GIT binary patch literal 1341 zcmaJ>TTc@~6#jFw^Y>mTq^Ny)@}R@m&*r zP!lo1ga?0sKgxKvTY5>+O?J=B?D@|5&NpY~_n*^W0J7K&p$EMR`c(8|fFXRut2}9N z^C0=Ea3nN`VPI7^b!UyCH=fuJB7mTRkctaX8Rm1`IuJR*OSQbPs=6j(FNI_2nw^n` zL2zj?1S^)*gl&r=1KSQ_2*V1(Dn>BMFwzx)0NMTQ&fdH9>pa7BuFD~otqRjgyLKvo ziwufp2yVKS5F(gRa7o1^rWmIFjU!COEf~6e$gmjCwMxztb@L$Aw&IJB@(Q2EjDlGe zR}k&cDPOZ4Q6_n$OKOe#U|lA6DV9SK1uv&OZu2UxBE~RCi5+1ID7Oq6*^WTdHa=Z)?h(#UqW>DUaB`1V$!ja-9n2urxGU|GcttT2q#>**xmRqW9* zOk#LW#fg1}zKkpihT)uUiXFFH5Y`?q7~YB{G-(xgTf3-e{_ZjaRyCv19-+K+Elq6b z9wySI{)$J706W5VHlz5 z0oQA86>TmUh$aBEyZG?EZZ#ITUn1&*XxTWt%G&vHRb@bLkD zl<}0EVRPorT%ZLVCoF`_?BNL97YY_i}+Q>HD^emW;~d-kwKPVJgb@O zE77%Hosf8N)ku2o?BHT)qHGA+mB_T;J0!JX>6qF5b&5)AmtiAiDQ{Aui; zkZOyCYd?$(>=AM+Z3MgNc^ouc6^0xV(*JGaNbG8okd9pZQ+efL?}D-p`6ZG>wkdo| z*jXH-V5a&Gs+Q(3swZ=7ye%8IrRI^4x{&$-6Q4-qO9l#}Y1qN;5{oDaTCy^QBKBea E0o1@$0RR91 literal 0 HcmV?d00001 diff --git a/out/LargeLeakyService$RequestHandler$1.class b/out/LargeLeakyService$RequestHandler$1.class new file mode 100644 index 0000000000000000000000000000000000000000..3be56048e5d34d2dd27d0c46462d79095954f138 GIT binary patch literal 1140 zcmah|*-{fh6g`~;h6w|NDB!{-2FQXSu4s^#6apHeWh^Lp>rC1(F!bO|PYU1s4gCwN zKrJ8q06)s|_7Dp&f>SlU-1YQ5_x}9#{Re<$ESZQQZXjWy1Dy;LW$rgbS@7NWRpB=s zTV!5|ccBQ>DwlOv_?dYIqsiTjtq!#Lx%MSbDsiBSNp!(9khIVZi=lsqH~Czs9Cz+T zZAaKDVCeNJh2x8gCo7@rGE8O5k(tY7qh(giwTqJIMW2C`g?^+NQh)m}bS^s5QA-T* zY;L0qmkGP=3NFL_BnB~L;EIK-xOT?3s(eQ_$d^EIm*5Tj3(b~^VT>3UwJ?TphVlOu zf+6PA8Q7Y3oUm{c8HU7Lms2XXWnhY7tUbnZQ%F^I0wtvI8CJ6A7raGK@qCLvl;k!j zbIGd(xg<%g3$^XlUvi)C2_<|gfLV1KlB>`swc*JK6T|fBvK$w! z*v^s8)l*g6-d*SWtrW?s7y7n%>garfZI7PSJQB2JX}exPTpS}ZaQC7zx1Mj|J{}Nx z+flnROkPm2)Ai`8=pOe1W#AEYX&Wd8YfTadC6@<*&`o=GE=~lksPybJvQ4=H+1M&6grIromXID6$5yN z5v*gDByq42Qq4t%w=qw90(Wo^4n>OyU6W^%ur+;h)4_dDm@`}yAwp8yo_RL1}gXgFwK5GjF?7jjEZ zyV7e;-&uX3>_{Lr=Xg$ZL*PIzUr%EQX$`u8LoftRmt@dXB_-Flt18%XY?ZyQ{%NT& zvV1R6FS&fcX$Xj7hVG6S7{-Xe=!RTZRIp|Az=_uQ6-LsFLV;tsV!pH&LNy8;uW6={ z!FL+IH*gHc1&($(6&SQ`S&M}@psvZRCG9m;mY!^?z|`=Az<3{}MqRosrDF^~YB*_N z9H%-cscwgn+F%5BD+rVq)tn7gc3jtC=wxR}$0$z6OrBvxx#Cj3uHziC8qOQ|2|o** z>f^W_9j$D($k|M;n}mKFOJX+jbq#rev)vBb30ah0!&O1{ijEwn;vg>w+|Knd+quB( zuhGsiea9KPWMCSX1=59jp;CJ`zg!hK*<*2Gi_FcpLQ}_8T+=XP;1~QVFxDOV7gd3w zKs6nb9|&Z7#k{LoiZwhYIp+O${GnmifEkx{4_eBK#bt+<@475=pN5A6W&1(nZs6N0 z3{^wJ^>0;pn^1v+mfuiZUUEEDX>F{kpe9#c{z!k7j0s^1E1wcy07 zk9YBLDeeq%QDJ{A`XLKtWZw6qFbd=*x3lIq!Xeyd>DkfCSuvbKQO6Sg)=-KYuT1Lp zTFdda{B<>*bXd}h3o_HflRodktSysFmenF2=9V}dPLGC^#*Bb_92et)wI9NHC=7TMGOKXXH03QWVYz(g0} z*^aUjIQ*47@^ukuSYfvNaL`GfhQ~Y)zOBjZYC&%tA!I5YJY+6Np4%5QM=!bgm*#DCu7#11Cj;p|7`Rwk!* zFj;wnix=PF%6t5_XL*daL3=v=Ou#}O_i!0CT*ET0&vR~ofB)LW3@;e)!Vp;GyMPL% vCB#=q26u?(hJ&*64 zbM@Z8ul*gsIIilr1rZG`23pZ3(0SfmHj}pLEF@3NpO<-Gpl#H0EdO4CNMdMCLsVce zYkCVZE6t?~Iq5B1c^N+?9|>gHpER9fl?8eo`DiY`C<}ot3$u%! zGz%wP*A_UO$P8t-l-^*ksi}IIQZC(Qpbz~5v2sL&F0yjxQIa7kT{p_3Iiwc{A9iUGlE@5!M29jNsF% z8jcY9iOd6ILvsqwM-8Mf%B{r^;sW~;AC1+jzSPiM4EN%58a{8}7{(gPD|eyn%OVr) z2Fy%PB?gw8Jn31EpYw^+Vu-i<43_BxA(B3po*Y+RG?z%)Bo?Pl$E3e3kV#}TOc^+i zEDPl3>;f%i>oF#W)t%{rz>I+tI4N*j(Oi;rByaj>EPs(t#Hiz|ib`Z^l@kV<##+t- z&pm~lhFJrrF-Hi5nP_x~2rMc0Y(l%I9t>3#Dqj0=zk#y~&Bk;lo6S@qTZ8;zog5Du z_zKRcvazKs(S^j6YE5RX9c<*^^MaBu3*#$!S@JE{(UC_%gEa6k78=q~=?nrL)fQ6w z-Q7@um0h};hh^ZrD%}|Uk>NQyY!o#(23!^Rx>j0okKnPTv*0gw5oeF6Ftc^^Y;>W< zSo#PwEE{-Kow2t$htrZ#&zIA#ZL>?e)THGr&l)ZW>}-}8UuI_<3(7iHD$V(82ELAO z(1Y>0@tN6kW2e~{c5Q*KnqY{m2&0{L3k1yetmVj=pg1qRS=E*JqG(!S(|Vw;&Nosk zpl{@%tl?WVh=hJ`ykQ1%zL{T|HcM5ly#-lR;Pco+W&+zj>4p*K^svq1e8G%|y)%Bd zKy);3S7();a|16gCoE-GZ&NT1DsT$4&v0fv7u`a+6K&hu@N+d7UNrDsv_Psc=KAv={4ty)HSNmz5)LE?Yk# ztD4iMZRadsrWlgfb-aO#8s1a|_m)7sDI^tyR$AReEhsPk|HDwNjQV1!+__RWbBTR1 z;}B9Lt<^ZuPzQJx;|^sAo4r+zi{NW0YSF{He8k+yL}@utf43H~~Q2%mty;j0kC z>(I_#MeGXn;g)yNc?G?f_)g$SuG`27YHGnQuDcP#H}MqDe1f|NNY&{b>u9Hfo!PtA zFmR)2AEGqCz)n0())uOLhF{*^xGE+8;w7Pl)Ea&Vz1MI%5bMU?RmA^_VGU;@qXS)s zU&Zt~M$Qg&ed=$~tGH*Htor^nd}g4tP(C}7f(|krl`CrrQRyrqxjwi=b zn3u;c_z`}>{XT-> z*ZlqzKjZf`9{r1O{UzS!+bKG&5s<$p9U=V#=_KhNNqy2kk*<*bne=(mzmUE_dZ{f& N{guxu-eLaw{{t5Nrkwx) literal 0 HcmV?d00001 diff --git a/out/Main.class b/out/Main.class new file mode 100644 index 0000000000000000000000000000000000000000..daeab4948a025465d1d556061c3b354302ad42a0 GIT binary patch literal 440 zcmZuuO;5r=5Pb`U(pm*U@RI}%8t@=L0Apf|#sex5!^MMHU}=`LyQa1newH3I@!${e zM;T}9!I-dznYTM{-kZ#ReSdrcI7i(@23Z@HhaB<@r5T^|PQaDlxf#vGIA+KnOC{qI zhHR@nbWng}!}YKN?;m}zc#1_xES*qOwU-J>-RVbC#e+B!JnR;*#&B>cfkq#SY zdLnMcT#DyYU#R$@AMk}A=|oMME=pLpQT9;5218|u9)ta8)B{C^tzXA MetricsCollector.collect(), 0, 5, TimeUnit.SECONDS); + + // simulate incoming traffic, register a per-request listener unintentionally + for (int i = 0; i < Integer.MAX_VALUE; i++) { + RequestContext ctx = new RequestContext(i, new byte[128 * 1024]); // 128 KB + requestHandler.handle(ctx); + + if (i % 100 == 0) { + System.out.println("processed " + i + ", cache size " + CACHE.size()); + } + + try { + TimeUnit.MILLISECONDS.sleep(50); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + } + + /** + * Simulates the HTTP or message handler. + * It processes requests, updates cache, and registers a listener on the global event bus. + * The listener is the source of unintended object retention in this demo. + */ + public static class RequestHandler { + + public void handle(RequestContext ctx) { + // process and cache some derived value + String key = "ctx:" + ctx.id; + CACHE.put(key, new CachedValue(ctx.id, System.currentTimeMillis())); + + // register a per-request listener on the global event bus + // BUG: this anonymous listener captures ctx implicitly, and it is never unregistered + EventListener listener = new EventListener() { + @Override + public void onEvent(String type, Object payload) { + // use ctx so the listener holds a strong reference to the whole RequestContext + if ("cleanup".equals(type)) { + if (ctx.id % 2 == 0) { + // pretend to do some cleanup + ctx.flag = true; + } + } + } + }; + + // LEAK POINT: registering listener on a long lived EventBus without unregister + EVENT_BUS.register(listener); + + // pretend other work + MetricsCollector.recordProcessed(); + } + } + + /** + * Simple global event bus that holds strong references to listeners in a list. + * Intended to be long lived. Listeners must be removed explicitly. + */ + public static class EventBus { + private final List listeners = Collections.synchronizedList(new ArrayList<>()); + + public void register(EventListener listener) { + listeners.add(listener); + } + + public void unregister(EventListener listener) { + listeners.remove(listener); + } + + public void publish(String type, Object payload) { + // copy to avoid ConcurrentModificationException + List snapshot; + synchronized (listeners) { + snapshot = new ArrayList<>(listeners); + } + for (EventListener l : snapshot) { + try { + l.onEvent(type, payload); + } catch (Exception ignored) { + } + } + } + + public int listenerCount() { + return listeners.size(); + } + } + + public interface EventListener { + void onEvent(String type, Object payload); + } + + /** + * Cache manager that is intended to hold a small number of short lived entries. + * In this demo it becomes large because callers add entries without eviction. + */ + public static class CacheManager { + private final Map map = new ConcurrentHashMap<>(); + + public void put(String key, CachedValue value) { + map.put(key, value); + } + + public CachedValue get(String key) { + return map.get(key); + } + + public int size() { + return map.size(); + } + + public void clear() { + map.clear(); + } + } + + public static class CachedValue { + public final int id; + public final long ts; + + public CachedValue(int id, long ts) { + this.id = id; + this.ts = ts; + } + } + + /** + * Simple metrics collector that runs in background. + */ + public static class MetricsCollector { + private static volatile long processed = 0; + + public static void recordProcessed() { + processed++; + } + + public static void collect() { + // publish a periodic cleanup event + EVENT_BUS.publish("cleanup", null); + System.out.println("metrics processed " + processed + ", listeners " + EVENT_BUS.listenerCount()); + } + } + + /** + * Request context carries a payload and some state. + * It is deliberately heavy to make retention obvious in heap dumps. + */ + public static class RequestContext { + public final int id; + public final byte[] payload; + public boolean flag; + + public RequestContext(int id, byte[] payload) { + this.id = id; + this.payload = payload; + } + } + + /** + * Example demonstrating a NullPointerException (NPE). + * This method intentionally dereferences a null reference so reviewers + * can see the exact pattern that leads to an NPE. Do NOT call this + * from production code — it's only for illustration / demo purposes. + */ + public static void demonstrateNullPointer() { + String maybeNull = null; + // NPE POINT: the following line dereferences `maybeNull` which is null + // and will throw a java.lang.NullPointerException at runtime. + int len = maybeNull.length(); + // unreachable if NPE occurs, but left here for clarity + System.out.println("length: " + len); + } +} diff --git a/src/NpeRunner.java b/src/NpeRunner.java new file mode 100644 index 0000000..65a3ba3 --- /dev/null +++ b/src/NpeRunner.java @@ -0,0 +1,5 @@ +public class NpeRunner { + public static void main(String[] args) { + LargeLeakyService.demonstrateNullPointer(); + } +}