Was discussing code clarity when another developer. He said using local variables increases memory use. We argued that they will be garbage collected. and especially a bad idea if logging statements calls a function that hits db/ other external resource.
But following sample code seems to support what he is saying - even after calling GC using jconsole, still see that the class Worker uses less memory than Worked2. Any ideas why?
Free memory : 124629976 - worker 124784720
package test;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class ObjectLife {
public static void main(String[] args) {
// simple multi thread to mimic web app
// ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 3, 110, null, null);
Executor pool = Executors.newFixedThreadPool(3);
String type = "1";
if (args.length > 0) {
type = args[0];
}
Work w = null;
if ("1".equals(type)) {
w = new Worker();
} else {
w = new Worker2();
}
w.init(2);
System.out.println("w type " + w.getClass().getName());
Watch.me.print();
pool.execute(w);
pool.execute(Watch.me);
}
}
class Watch implements Runnable {
long prev = 0;
static Watch me = new Watch();
@Override
public void run() {
while (true) {
try {
Thread.sleep(1100);
} catch (InterruptedException e) {
System.out.println("Intrpt thread " + e);
}
print();
}
}
public void print() {
Runtime r = Runtime.getRuntime();
long free = r.freeMemory();
System.out.println("Free " + free + ", delta " + (free - prev));
System.out.println(", av " + r.maxMemory());
prev = free;
}
}
class Work implements Runnable {
double val = 0;
public void init(double val) {
this.val = val;
}
void do2() {
}
@Override
public void run() {
int cnt = 0;
while (++cnt < 175) {
do2();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println("Intrpt thread " + e);
}
}
System.gc();
System.out.println("Type, v3, : " + this.getClass().getName());
Watch.me.print();
}
}
class Worker extends Work {
public void do2() {
double local = ++val;
double local2 = local * 2;
System.out.println(" local " + local + " double " + local2);
}
}
class Worker2 extends Work {
public void do2() {
System.out.println(" local " + ++val + " double " + (val * 2));
}
}
Why does the class Worker take more memory - even after calling GC several times and then disconnecting Jconsole from process and let a few second pass? (check and print avaialable every 2 seconds.
Can see that the code is the same as super class of work and worker are the same except for do2() method
Note: I'm connecting from jconsole and calling GC after the Work loop is done. This GC call does work. Calls the MX bean and that can see available memory drop.
Side note : I even notice that if i disconnect jconsole from my app then wait 4-5 seconds - available memory goes up again (i guess over head of connecting to jconsole). At this time I make the measurement. Point is I do some work, wait for JVM to settle down then measure.
Video of running this program and freeing mem with jconsole https://www.youtube.com/watch?v=MadBdryX8uk&
jconsole is a free tol with JDK in the bin folder.
Edited class, here increased the loop count, and now the memory is same no matter which class is used!
Can someone else run it?
package test;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Run with param 1 and 2 or change type to "2" and "1" Available memory changes, used jconsole to change force garbage collection.
*
* See video https://www.youtube.com/watch?v=MadBdryX8uk
*/
public class ObjectLife {
public static void main(String[] args) {
// simple multi thread to mimic web app
// ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 3, 110, null, null);
Executor pool = Executors.newFixedThreadPool(3);
String type = "1";
if (args.length > 0) {
type = args[0];
}
Work w = null;
if ("1".equals(type)) {
w = new Worker();
} else {
w = new Worker2();
}
w.init(2);
System.out.println("w type " + w.getClass().getName());
Watch.me.print();
pool.execute(w);
pool.execute(Watch.me);
}
}
class Watch implements Runnable {
long prev = 0;
private int dieCnt = -1;
static Watch me = new Watch();
@Override
public void run() {
while (true) {
try {
Thread.sleep(1100);
} catch (InterruptedException e) {
System.out.println("Intrpt thread " + e);
}
if (dieCnt > -1) {
dieCnt--;
if (dieCnt == 0) {
System.exit(0);
}
}
print();
}
}
public void print() {
Runtime r = Runtime.getRuntime();
long free = r.freeMemory();
System.out.println("Pr v6 Free " + free + ", delta " + (free - prev) + ", av " + r.maxMemory());
prev = free;
}
public void countDown() {
dieCnt = 3;
}
}
class Work implements Runnable {
double val = 0;
double val3 = 0;
public void init(double val) {
this.val = val;
}
void do2() {
}
@Override
public void run() {
int cnt = 0;
while (++cnt < 475) {
do2();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println("Intrpt thread " + e);
}
}
System.gc();
System.out.println("Type : " + this.getClass().getName());
Watch.me.print();
System.out.println("oink");
try {
Thread.sleep(9100);
} catch (InterruptedException e) {
System.out.println("Intrpt thread " + e);
}
Watch.me.countDown();
}
}
class Worker extends Work {
public void do2() {
double local = ++val;
double local2 = local * 2;
System.out.println(" local " + local + " double " + local2);
val3 = local2 + 1;
}
}
class Worker2 extends Work {
public void do2() {
System.out.println(" local " + ++val + " double " + (val * 2));
val3 = (val * 2) + 1;
}
}
- I know local vars are more readable. I like them. I argue for them.
- Just curious about this behavior.