Plenty of existing code does things like:
log.info(“foo: “ + request.getFoo());
Rather than using fixed format strings and {} place holders. You’re not supposed to, but it’s far from uncommon.Plenty of existing code does things like:
log.info(“foo: “ + request.getFoo());
Rather than using fixed format strings and {} place holders. You’re not supposed to, but it’s far from uncommon.Why would you still do be doing that work? If a value goes into a no-op, then the value isn't computed. (In theory - I'm sure it doesn't always work out 100% of the time in practice.)
Are you saying that Java does or should "optimize" in such a way that it branches to the logic within each method called during runtime, inspects which code would be executed per possibly mutable runtime values (like log level), considers possible side effects (in-method and in parameters) then decides whether to even invoke the method at all?
One of the bigger wins with the JVM is assuming that a virtual method can be called statically if there are no derived classes. That's a huge win for every leaf class in the class tree. The assumption can change every time a class is loaded of course. The optimization is called devirtualization and it can be combined with inlining to get even bigger wins.
Should the JIT call incrementAndReturnFooCount if debug logs are disabled? This is a long-recognized pitfall of C preprocessor macros that look like function calls but may simply be defined away, causing unexpected behavior.
Did I fall into an alternate dimension where 90% of Java seems to only use preexisting objects now, and people hardly ever write their own code?
(No disrespect to the asker; it's just such a surprising question to stumble across, that mentally speaking, I had to deoptomize my mental model of the world to accommodate for people who may not have worked much with Java).
I get functional is all the rage, but wow. Default toString() impl is inherited from Object, and basically gives you class and instance number.
The whole point of a JIT is that it can make assumptions. 'Assumption' is literally the term used in the JIT to track things like this.
If logging was part of the language, one could simply rule by fiat that arguments shall not be evaluated unless logging is enabled, but log4j is just another collection of user-defined classes, and gets no special treatment.
I'm not saying that when it comes to the JVM's optimization behaviors that I'd jump without checking that there's water on Chris's assurance, but I'm not saying I wouldn't either.
To be clear, I am not suggesting that these sort of expressions are inherently insecure - that would depend, I think, on whether they involve user input in a way that allows an attacker to take control of the call. A hard-coded JNDI lookup from the arguments list of a log4j call might be inefficient, but no less secure than if made anywhere else.
Assuming a global mutable variable will never change is generally not a safe assumption.
I suppose, at least in principle, that something like the GraalVM AOT compiler has the option to scan all of the code to verify that it never is mutated. But HotSpot cannot, because it only JITs code as it is loaded.
Put your logging level behind a switch point and C2 will treat it as a constant but still let it be changed - that works today.
For example, an interface implemented by only 1 class might get the class inlined. If a second implementer pops up (which can happen at runtime for e.g. some dynamically generated class), all of this will get undone.
There was a series of small articles with all these things, but I can't seem to find them right now.
Java HotSpot can make and verify that assumption. And switch code when the level changes.
If (a) DoExpensiveStuff());
Becomes
When modifyA() RecompileIfWithNewAValue();
As long as you call the initial if more often than you modify (a), you’re fine (and the JVM was able to see that you called your if 10k times without modifying the value of (a) even once)
log.debug(xmldoc)
The debug method took a string, and Java was converting the xmldoc to a string. This was back in the 1.4/1.5 days.
log.Print(fixed_string)
log.Printf(format_string, args...)
Simple, unambiguous.If it were a special type then the compiler could do it.