Exploring Static Init

R

Roedy Green

After Thomas surprised me with the enum decompilation, I thought I
would have a look at a static init decompilation to see I what I
thought was true was indeed true.

Here is my little test program:

// experiment to see order of initialisation
public class Init2
{
static {
// define m multiple times
m = 1;
}
static final int inlined = 2;
static final int notInlined = Math.min( 3 , 4 );
static int m = 5;
static
{
m = inlined + 6; // effectively 8
m = notInlined + 7; // effectively 10
}
}


DJ compiles it like this:
public class Init2
{

public Init2()
{
}

static final int inlined = 2;
static final int notInlined;
static int m = 1;

static
{
notInlined = Math.min(3, 4);
m = 5;
m = 8;
m = notInlined + 7;
}


Javap decompiles it like this:


javap -c Init2

Compiled from "Init2.java"
public class Init2 extends java.lang.Object{
static final int inlined;

static final int notInlined;

static int m;

public Init2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

static {};
Code:
0: iconst_1
1: putstatic #2; //Field m:I
4: iconst_3
5: iconst_4
6: invokestatic #3; //Method java/lang/Math.min:(II)I
9: putstatic #4; //Field notInlined:I
12: iconst_5
13: putstatic #2; //Field m:I
16: bipush 8
18: putstatic #2; //Field m:I
21: getstatic #4; //Field notInlined:I
24: bipush 7
26: iadd
27: putstatic #2; //Field m:I
30: return

}


So what do we discover? Probably nothing new to the old timers.

1. static final int inlined = 2; gets inlined. It is treated like a
literal. The line m = inlined + 6; is effectively turned to m = 2+ 6;
which is collapsed further to m = 8;.

This is why you have to recompile the classes that use constants in
some other class when that base class changes the constant values. The
dependent code has to be re-inlined. The original source of the
constant value is lost in the class file.

2. The initialisations are done in strict order, whether they are
attached to a single line or are part of a static block. You might
reasonably have expected all the one liners to have been done first,
but that is not so.

This is why you have to be careful with field-ordering IDEs like
Eclipse. Changing the field order can change the dependency order
during initialization. There is no clever spreadsheet-like discovery
of dependencies. Initialisation proceeds mindlessly top to bottom.

3. You are allowed to initialise variables not defined yet in static
blocks, but you are not allowed to refer to variables not initialised
yet. This is because the definition and initialisation, though mixed
together in Java source, are separate in the class file. Note the m=1.

4. JavaC is not doing even the most rudimentary optimisation of
discarding a save to a variable immediately followed by a save of a
different value to that same variable.
 
M

Mike Schilling

Roedy Green said:
2. The initialisations are done in strict order, whether they are
attached to a single line or are part of a static block. You might
reasonably have expected all the one liners to have been done first,
but that is not so.

That is to say, the order is predictable. This is a good thing. (By the
way, the same is true of instance initialization: top to bottom.)
This is why you have to be careful with field-ordering IDEs like
Eclipse. Changing the field order can change the dependency order
during initialization.

Very true.
There is no clever spreadsheet-like discovery
of dependencies. Initialisation proceeds mindlessly top to bottom.

Again "predictably from top to bottom", and again, unless you want subtle
bugs introduced by the compiler's attempt to make sense out of code that
doesn't have an obvious single meaning, that's desirable..
 
T

Thomas Hawtin

Roedy said:
3. You are allowed to initialise variables not defined yet in static
blocks, but you are not allowed to refer to variables not initialised
yet. This is because the definition and initialisation, though mixed
together in Java source, are separate in the class file. Note the m=1.

This will be definite assignment/unassignment, which presumably works
the same way as for locals and instance variables. The JLS has a rather
dull chapter on the subject.
4. JavaC is not doing even the most rudimentary optimisation of
discarding a save to a variable immediately followed by a save of a
different value to that same variable.

IIRC, javac used to do more optimisation. You can easily see how class
file size and number of byte code instructions can go down. Great for
J2ME. However byte code optimisation can, apparently, make it more
difficult for HotSpot to optimise its intermediate representations.
Indeed, javac does some pessimisation, duplicating small finally blocks
for exception and normal cases.

Tom Hawtin
 
T

Thomas G. Marshall

Roedy Green coughed up:
After Thomas

please *always* supply a last name if you can when the first name is a
common one.

surprised me with the enum decompilation, I thought I
would have a look at a static init decompilation to see I what I
thought was true was indeed true.

Here is my little test program:
....[rip]...

So what do we discover? Probably nothing new to the old timers.

1. static final int inlined = 2; gets inlined. It is treated like a
literal. The line m = inlined + 6; is effectively turned to m = 2+ 6;
which is collapsed further to m = 8;.

This is why you have to recompile the classes that use constants in
some other class when that base class changes the constant values. The
dependent code has to be re-inlined. The original source of the
constant value is lost in the class file.

Yep. Hence the need for intelligent make{rs} able to garner such
dependencies out of source.

2. The initialisations are done in strict order, whether they are
attached to a single line or are part of a static block. You might
reasonably have expected all the one liners to have been done first,
but that is not so.

This is why you have to be careful with field-ordering IDEs like
Eclipse. Changing the field order can change the dependency order
during initialization. There is no clever spreadsheet-like discovery
of dependencies. Initialisation proceeds mindlessly top to bottom.

Correct, but I seem to remember eclipse not mucking with field orders when
there are explicit initializers. Am I wrong here? Seems to ring a bell....

3. You are allowed to initialise variables not defined yet in static
blocks, but you are not allowed to refer to variables not initialised
yet. This is because the definition and initialisation, though mixed
together in Java source, are separate in the class file. Note the m=1.

Interesting. I'm going to think on this---it doesn't seem right. By the
way, over time it seems that in the universe "definition" and "declaration"
are being used interchangeably. Oh well...

4. JavaC is not doing even the most rudimentary optimisation of
discarding a save to a variable immediately followed by a save of a
different value to that same variable.

I don't believe that it /strictly/ can, if I understand you correctly. The
static initializers happen at class load time which can occur at any point
in the runtime, which might occur at the same time some other thread is
attempting reading and mucking with those values.

This is my question then: Does access to variables mid-static initialization
get blocked until the initialization of the class is complete? If a static
block does this:

public static int a = 5;
static { a = 6; a = 7; }

is access to "a" disallowed from outside until the static initialization
period is over?
 
R

Roedy Green

IIRC, javac used to do more optimisation. You can easily see how class
file size and number of byte code instructions can go down. Great for
J2ME. However byte code optimisation can, apparently, make it more
difficult for HotSpot to optimise its intermediate representations.
Indeed, javac does some pessimisation, duplicating small finally blocks
for exception and normal cases.

This is opening the door for byte code optimisers where the code will
eventually be interpreted. The save x save x optimisation is safe,
simple to find, and I would think unlikely to confuse a hotspot
optimiser. Perhaps it does in some special case so they turn it off
generally.
 
M

Mike Schilling

Roedy Green said:
This is opening the door for byte code optimisers where the code will
eventually be interpreted. The save x save x optimisation is safe,
simple to find, and I would think unlikely to confuse a hotspot
optimiser. Perhaps it does in some special case so they turn it off
generally.

Or pehrpas bytecode optimization is considered of little importance in the
scheme of things, so little or no resources are devoted to it.
 
R

Roedy Green

Or pehrpas bytecode optimization is considered of little importance in the
scheme of things, so little or no resources are devoted to it.

When Hotspot gets hold of it, the problem is solved. The problem only
exists when you run the code elsewhere, e.g. in a cellphone.

There you fight for every byte. I'm surprised at Sun being so
cavalier. Perhaps they are trusting some third party will step in.

The tools to do it may be masking as obfuscators which also try to
reduce footprint.
 
R

Roedy Green

Correct, but I seem to remember eclipse not mucking with field orders when
there are explicit initializers. Am I wrong here? Seems to ring a bell....

Eclipse has a tidying feature to put code in some order you specify,
e.g. collecting publics together alphabetically. You have to be aware
that if you use this feature, you can disturb delicate ordering that
static init depends on.

If you want to make your code robust to survive such tidying, put
tricky initialisation in a static { } block where it won't be
reordered.

ditto for instance init.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top