I'm the lead author of ORMLite which uses Java annotations on classes to build database schemas. A big startup performance problem for our package turns out to be the calling of annotation methods under Android 1.6. I see the same behavior up through 3.0.
We are seeing that the following simple annotation code is incredibly GC intensive and a real performance problem. 1000 calls to an annotation method takes almost a second on a fast Android device. The same code running on my Macbook Pro can do 28 million (sic) calls in the same time. We have an annotation that has 25 methods in it and we'd like to do more than 50 of these a second.
Does anyone know why this is happening and if there is any work around? There are certainly things that ORMLite can do in terms of caching this information but is there anything that we can do to "fix" annotations under Android? Thanks.
public void testAndroidAnnotations() throws Exception {
Field field = Foo.class.getDeclaredField("field");
MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
long before = System.currentTimeMillis();
for (int i = 0; i < 1000; i++)
myAnnotation.foo();
Log.i("test", "in " + (System.currentTimeMillis() - before) + "ms");
}
#Target(FIELD) #Retention(RUNTIME)
private static #interface MyAnnotation {
String foo();
}
private static class Foo {
#MyAnnotation(foo = "bar")
String field;
}
This results in the following log output:
I/TestRunner( 895): started: testAndroidAnnotations
D/dalvikvm( 895): GC freed 6567 objects / 476320 bytes in 85ms
D/dalvikvm( 895): GC freed 8951 objects / 599944 bytes in 71ms
D/dalvikvm( 895): GC freed 7721 objects / 524576 bytes in 68ms
D/dalvikvm( 895): GC freed 7709 objects / 523448 bytes in 73ms
I/test ( 895): in 854ms
EDIT:
After #candrews pointed me in the right direction, I did some poking around the code. The performance problem looks to be caused by some terrible, gross code in Method.equals(). It is calling the toString() of both methods and then comparing them. Each toString() use StringBuilder with a bunch of append methods without a good initializing size. Doing the .equals by comparing fields would be significantly faster.
EDIT:
An interesting reflection performance improvement was given to me. We are now using reflection to peek inside the AnnotationFactory class to read the list of fields directly. This makes the reflection class 20 times faster for us since it bypasses the invoke which is using the method.equals() call. It is not a generic solution but here's the Java code from ORMLite SVN repository. For a generic solution, see yanchenko's answer below.
Google has acknowledged the issue and fixed it "post-Honeycomb"
https://code.google.com/p/android/issues/detail?id=7811
So at least they know about it and have supposedly fixed it for some future version.
Here's a generic version of Gray's & user931366's idea:
public class AnnotationElementsReader {
private static Field elementsField;
private static Field nameField;
private static Method validateValueMethod;
public static HashMap<String, Object> getElements(Annotation annotation)
throws Exception {
HashMap<String, Object> map = new HashMap<String, Object>();
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
if (elementsField == null) {
elementsField = handler.getClass().getDeclaredField("elements");
elementsField.setAccessible(true);
}
Object[] annotationMembers = (Object[]) elementsField.get(handler);
for (Object annotationMember : annotationMembers) {
if (nameField == null) {
Class<?> cl = annotationMember.getClass();
nameField = cl.getDeclaredField("name");
nameField.setAccessible(true);
validateValueMethod = cl.getDeclaredMethod("validateValue");
validateValueMethod.setAccessible(true);
}
String name = (String) nameField.get(annotationMember);
Object val = validateValueMethod.invoke(annotationMember);
map.put(name, val);
}
return map;
}
}
I've benchmarked an annotation with 4 elements.
Millisecond times for 10000 iterations of either getting values of all of them or calling the method above:
Device Default Hack
HTC Desire 2.3.7 11094 730
Emulator 4.0.4 3157 528
Galaxy Nexus 4.3 1248 392
Here's how I've integrated it into DroidParts: https://github.com/yanchenko/droidparts/commit/93fd1a1d6c76c2f4abf185f92c5c59e285f8bc69.
To follow up on this, there's still a problem here when calling methods on annotations. The bug listed above by candrews fixes the getAnnotation() slowness, but calling a method on the annotation is still a problem due to the Method.equals() issues.
Couldn't find a bug report for Method.equals() so I created one here:
https://code.google.com/p/android/issues/detail?id=37380
Edit:
So my work around for this (thanks for the ideas #Gray), is actually pretty simple.
(this is trunkcated code, some caching and such is omitted)
annotationFactory = Class.forName("org.apache.harmony.lang.annotation.AnnotationFactory");
getElementDesc = annotationFactory.getMethod("getElementsDescription", Class.class);
Object[] members = (Object[])getElementDesc.invoke(annotationFactory, clz); // these are AnnotationMember[]
Object element = null;
for (Object e:members){ // AnnotationMembers
Field f = e.getClass().getDeclaredField("name");
f.setAccessible(true);
String fname = (String) f.get(e);
if (methodName.equals(fname)){
element = e;
break;
}
}
if (element == null) throw new Exception("Element was not found");
Method m = element.getClass().getMethod("validateValue");
return m.invoke(element, args);
You mileage will vary based on use, but in may case this was about 15-20 times faster then doing it the "right way"
I think if you manage to change the RUNTIME retention policy, it should not be that slow.
EDIT: I know, for your project that may not be an option. Perhaps it is more a problem of what you are doing with that annotation rather than bad performance in general.
Related
After making some changes to an application it suffered a significant performance degradation and on investigation one of the most frequently called methods is no longer being compiled. Turning on: -XX:+LogCompilation shows that before the change, this method was: queued for compilation, compiled, and then successfully inlined into callers; whereas after the change, there is no record of a compilation attempt and the inlining attempt says:
inline_fail reason='not compilable (disabled)'
The original method is as follows, where _maxRepeats is an instance variable declared as a Map (no generics, code written a long time ago), used such that the key was an object of class DadNode and the value was an Integer.
private int cnsGetMaxRepeats(DadNode dn) {
if (_maxRepeats != null) {
Integer max = (Integer)_maxRepeats.get(dn);
if (max != null) {
return max;
}
}
return dn.getMaxOccurs().getValue();
}
The amendment involved changing the _maxRepeats map to use generics:
Map<Integer, Integer>
and a new parameter was added to the method:
private int cnsGetMaxRepeats(int childIdx, DadNode dn) {
if (_maxRepeats != null) {
Integer max = _maxRepeats.get(childIdx);
if (max != null) {
return max;
}
}
return dn.getMaxOccurs().getValue();
}
Using explicit calls to Integer.valueOf and Integer.intValue to avoid autoboxing make no difference; the method is still not compilable.
I can "poke it with a stick" until I get a solution which does what I want (and is also compilable), but what are the criteria behind this disabling?
I think a basic mistake on my part - the log with the "compilation disabled" method was produced when running debug through IntelliJ (although with breakpoints muted). I expect IntelliJ disables compilations for methods with breakpoints in, even when muted.
So to answer my own question, I have no reason to think that anything apart from explicitly disabling compilation will do so.
I'm new to WeakReferences, so I'm just trying to understand this. I threw together this short snippet of code to test their behavior. I plan to use them in a project I'm working on, because I need to track references to objects on a temporary basis. ie, I want to have a reference to them in a collection so long as they're not being used anywhere else.
So far, though, this short test code sample hasn't been working like I want it to. No matter how long I wait, weakRef.get() always returns the value, it never returns null.
Please help me figure out what I'm doing wrong. Thanks.
public static void main(String[] args) throws Exception{
String s = "Hello World!";
WeakReference<String> weakRef = new WeakReference<>(s);
System.out.println("Original1: " + s);
System.out.println("Weak1: " + weakRef.get());
s = null;
Thread.sleep(10000);
Runtime.getRuntime().gc();
int count = 0;
while(weakRef.get() != null){
System.out.println("Not null " + count);
count++;
Thread.sleep(1000);
}
System.out.println("Null");
}
Edit: Updated my code sample. I even tried manually invoking the garbage collector, and the the final println still outputs "Weak2: Hello World!".
Edit 2: Changed it again. Moved the 10 second wait to before the garbage collection, then included the loop. The loop keeps running so far for 48 seconds (before I decided to close it) after the garbage collection.
In Java, string literals, like your "Hello, World!" are "interned"; that is, they are put in a cache for reuse. As long as a loaded class depends on one of these interned strings, it won't be reclaimed by the garbage collector, and that is what's preventing your WeakReference from being cleared. Try this instead:
WeakReference<String> weakRef = new WeakReference<>(new String(s));
Of course, a string that isn't assigned from a literal value in your source code—for example, a String created from file contents—will be garbage collected at an appropriate time. You just picked a subtly tricky example to test.
Is "ResultSet" considered to be an ArrayList? I'm talking about jdbc. If no, then
how do I put the information i get from my DB using the
while (result.next()) {
....
}
syntax into an ArrayList called something like hotelResult?
I hope it was understandable.
A ResultSet is not an ArrayList. Rather, it is a special object (Interface) to hold data retrieved by queries via JDBC connections.
A ResultSet object cannot be updated, and can only be traversed forward... not back. By default, you can only iterate through it once, from the first row to the last (though with a bit of coding, you can generate a ResultSet object that can be edited and traversed bi-directionally).
The records stored within a ResultSet object can easily be placed within an ArrayList. Here is an example on how you can do this:
Connection con = ... ;
Statement stmt = ... ;
ResultSet results = stmt.executeQuery("...");
//Stores properties of a ResultSet object, including column count
ResultSetMetaData rsmd = results.getMetaData();
int columnCount = rsmd.getColumnCount();
ArrayList<String> hotelResultList = new ArrayList<>(columnCount);
while (results.next()) {
int i = 1;
while(i <= columnCount) {
hotelResultList.add(results.getString(i++));
}
}
NOTE: This example assumes a single String being returned in the query, such as a Hotel name. You will likely want to hold multiple pieces of data about each hotel, in which case you would create a "Hotel" object, and then create the ArrayList as a List of Hotel objects. By using a rowmapper, each hotel object can be populated with the associated data.
In addition, using one of the popular JDBC frameworks to handle JDBC connections, queries, and result sets can simplify the process further.
I will help u out :)!
Create the needed variables in the class see my example :)
public class HotelData {
private String hotelName = null;
private int hotelTelephone = 0;
public HotelData(String hotelName, int hotelTelephone) {
this.hotelName = hotelName;
this.hotelTelephone = hotelTelephone;
}
}
Now create the ArrayList:
public ArrayList<HotelData> hotelResult = new ArrayList<HotelData>();
With the while method now:
while(result.next()) {
hotelResult.add(new HotelData(result.getString("Enter columnname"), result.getInt("Enter colummname")));
}
Hope this will help u buddy :)! If u need to get the data from the ArrayList u can simply write ur own get methods in the HotelData class!
No, ResultSet is not considered an ArrayList but rather a table.
If hotelResult for example has the type of String you can fill the list with this piece of code(if the column from the ResultSet is a String).
while(result.next()) {
hotelResult.add(result.getString("Enter the columnname here");
}
For each datatype there is a method to get the value from the ResultSet.
Look in the Java API for the different kinds of methods.
i believe that will clear- ArrayList hotels holds objects of HotelDtos
public class HotelDto {
private String hotelName;
private String hotelAddress;
private int hotelRank;
public String getHotelName() {
return hotelName;
}
public void setHotelName(String hotelName) {
this.hotelName = hotelName;
}
public String getHotelAddress() {
return hotelAddress;
}
public void setHotelAddress(String hotelAddress) {
this.hotelAddress = hotelAddress;
}
public int getHotelRank() {
return hotelRank;
}
public void setHotelRank(int hotelRank) {
this.hotelRank = hotelRank;
}
}
public class HotelDao {
public List<HotelDto> getHotlInfo(String hotelName) {
List<HotelDto> hotels = new ArrayList<HotelDto>();
try {
String query = "SELECT hotelName, hotelAddress,hotelrank " + "FROM HOTELS_TABLE "
+ "WHERE hotelName = " + "'" + hotelName + "'" + " ";
ResultSet resultSet = DBConnection.getDBConnection().createStatement().executeQuery(query);
int i = 0;
while (resultSet.next()) {
HotelDto hDto = new HotelDto();
hDto.setHotelName(resultSet.getString(1));
hDto.setHotelAddress(resultSet.getString(2));
hDto.setHotelrank(resultSet.getInt(3));
hotels.add(i, hDto);
i++;
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
return hotels;
}
}
}
To answer your first question, u don't really need the HotelData class.
The only thing this class does is holding the data nice and clean in one Object (for each specific hotel).
If you implement it your way, you must cast all the values to Strings first(if they do not contain String values) before u can store your items in the hotelInfo list. This is because the hotelInfo list has the String type, in my implementation this is also not needed(the casts) because i have created a constructor with one String value and one int value.
If u want your example to work implement it like this:
not this: hotelInfo.add(result.getString("hotelNo"));
but like this: hotelInfo.add("" + result.getInt("hotelNo")); //Notice the cast here!
If you're asking how I can get the database values which is added in my list .Here is the solution below
pom.xml file add below snippet of dependency
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
call the method from the main program .
List output = readRows(rs);
here rs is the ResultSet object, readRows() is the method
below is the code snippet for the readRows() method
private static List<List<Object>> readRows(ResultSet rs) throws SQLException
{
ImmutableList.Builder<List<Object>> rows = ImmutableList.builder();
int columnCount = rs.getMetaData().getColumnCount();
while (rs.next()) {
List<Object> row = new ArrayList<>();
for (int i = 1; i <= columnCount; i++) {
row.add(rs.getObject(i));
}
rows.add(row);
}
return rows.build();
}
I would like to complete above technical solution with performance tuning advice.
Everyone didn't mention performance implication when storing entire SELECT output into program level variable in Java (applicable to any programming language).
Commercial DB size has growth to 1TB easily, and a single table is normal to be 300GB. iPhone 14 with 512GB is available today as well, in case you think 300GB of data is unlikely for the output of the SELECT statement.
Putting the table size of 1TB in mind, storing the entire SELECT output into simple variable like array, will indirectly used up 1TB of RAM. In Java, that is consider Java heap memory.
Certainly they are members who suggest storing the data in array of HotelDao class which contains setHotelName(), getHotelName(), etc. This might double its Java internal memory usage from 1TB to 2TB.
Running any Java application, you have to specify its heap memory with Java parameter -Xmx (assuming only heap memory involve) during application startup. If you are hosting it in J2EE, then it could be Apache Tomcat startup, IBM WebSphere Application Server startup, JBoss, etc. If you are doing it via reporting server, e.g. Cognos, Informatica, then they have their own configuration to define -Xmx parameter.
Next, multi-user, multi-instance, concurrent access is norm in today's technology. If multiple users are trigger from GUI to request to run the same SELECT statement concurrently, e.g. flight booking system, reporting, then there will be multiple instance of the same SELECT with 1TB table size, and minimum 1TB Java array variable size in Java heap memory. Imagine 10 concurrent users, such as Expedia, will easily created 10 of such array variable, each of 1TB. The total variable size will be 10TB, if there is only 1 such SELECT-Array design in the entire program.
The problem here it is impractical to define 2TB of Java heap memory during Java application startup. Configures 10TB Java heap memory is way beyond the hardware limitation of today's RAM max size, unless you are looking for heavy paging to the OS swap memory area.
Neither Eclipse Temurin nor Oracle Java 19 document the default max heap size (-Xmx) anymore, but Eclipse Java 9 (https://www.eclipse.org/openj9/docs/openj9_defaults/) indicated the default is 25% RAM size. In other word, 128GB RAM machine will allocate 32GB. Either value will not even fit 1TB, and immediately the entire Java application crashed once it fully used up 32GB (default), or entire RAM, 128GB.
Certainly Windows admin will tell you Windows will auto tune its pagefile as needed. My question will be how poor will be business users' experience when finding a hotel in Expedia takes minutes. Even Windows allow to allocate 10TB pagefile, we have to consider overall application performance (end user experience) degradation with the trade off of OS paging to disk; due to Java application programmer storing entire SELECT output in local variable, and even bigger if stored as Java class object.
In entire StackOverflow.com, it is often members do not consider application performance when designing JDBC application, and consider about volume size. The latest Intel workstation single socket CPU can only accommodate 128GB RAM DDR4 (ref Xeon E-2388G). I have not see any programmer who have access to 1TB RAM top end server, even that can't handle Java application that need 10TB RAM due to this design
Note: Latest Intel desktop CPU Core i9 14th gen that supports DDR5 - max RAM size 128GB RAM today (2022-10). Ref www.crucial.com
I'm working on a small scene graph implementation in Java 8. The basic scene node looks something like this:
public class SceneNode {
private final List<SceneNode> children = new ArrayList<>();
protected Runnable preRender;
protected Runnable postRender;
protected Runnable render;
public final void render() {
preRender.run();
render.run();
for (Renderable child : children) {
child.render();
}
postRender.run();
}
}
This works fine if the Runnables default to () -> {}. However, alternatively I could allow them to be null, but that means that render() method has to look like this:
public final void render() {
if (null != preRender) { preRender.run(); }
if (null != render) { render.run(); }
for (Renderable child : children) {
child.render();
}
if (null != postRender) { postRender.run(); }
}
So my question is, is the implicit cost of the branching introduced by the null check likely to cost more or less than whatever the JVM ends up compiling an empty lambda into? It seems like it should end up costing more to check for null, because a potential branch limits optimization, while presumably the Java compiler or JVM should be smart enough to compile an empty lambda into a no-op.
Interestingly, it seems that checking for null is a little bit faster, than calling an empty lambda or an empty anonymous class, when the JVM is run with the -client argument. When running with -server, the performance is the same for all approaches.
I have done a micro benchmark with Caliper, to test this.
Here is the test class (latest Caliper form git necessary to compile):
#VmOptions("-client")
public class EmptyLambdaTest {
public Runnable emptyLambda = () -> {};
public Runnable emptyAnonymousType = new Runnable() {
#Override
public void run() {}
};
public Runnable nullAbleRunnable;
#Benchmark
public int timeEmptyLambda(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
emptyLambda.run();
dummy |= i;
}
return dummy;
}
#Benchmark
public int timeEmptyAnonymousType(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
emptyAnonymousType.run();
dummy |= i;
}
return dummy;
}
#Benchmark
public int timeNullCheck(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
if (nullAbleRunnable != null) {
nullAbleRunnable.run();
}
dummy |= i;
}
return dummy;
}
}
And here are the benchmark results:
Running with -client
Running with -server
Is defaulting to an empty lambda better or worse than checking for a potentially null lambda?
This is essentially the same as asking if it is better to test for a null String parameter or try to substitute an empty String.
The answer is that it depends on whether you want to treat the null as a programming error ... or not.
My personal opinion is that unexpected nulls should be treated as programming errors, and that you should allow the program to crash with an NPE. That way, the problem will come to your attention earlier and will be easier to track down and fix ... than if you substituted some "make good" value to stop the NPE from being thrown.
But of course, that doesn't apply for expected null values; i.e. when the API javadocs say that a null is a permissible value, and say what it means.
This also relates to how you design your APIs. In this case, the issue is whether your API spec (i.e. the javadoc!) should insist on the programmer providing a no-op lambda, or treat null as meaning the same thing. That boils down to a compromise between:
API client convenience,
API implementor work, and
robustness; e.g. when using the value of an incorrectly initialized variable ...
I'm more concerned about the implications of the runtime performance of using an empty lambda vs using a null and having to do a null check.
My intuition is that testing for null would be faster, but any difference in performance will be small, and that the chances are that it won't be significant to the overall performance of the application.
(UPDATE - Turns out that my intuition is "half right" according to #Balder's micro-benchmarking. For a -client mode JVM, null checking is a bit faster, but not enough to be concerning. For a -server mode JVM, the JIT compiler is apparently optimizing both cases to native code with identical performance.)
I suggest that you treat that you would (or at least should) treat any potential optimization problem:
Put off any optimization until your application is working.
Benchmark the application to see if it is already fast enough
Profile the applications to see where the real hotspots are
Develop and test a putative optimization
Rerun the benchmarks to see if it improved things
Go to step 2.
Consider such method:
#Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
for (final Room room : map)
{
sb.append(room.toString());
sb.append(System.getProperty("line.separator")); // THIS IS IMPORTANT
}
return sb.toString();
}
System.getProperty("line.separator") can be called many times.
Should I cache this value with public final static String lineSeperator = System.getProperty("line.separator")
and later use only lineSeperator?
Or System.getProperty("line.separator") is as fast as using a static field?
I see your question as presenting a false dichotomy. I would neither call getProperty every time, nor declare a static field for it. I'd simply extract it to a local variable in toString.
#Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
final String newline = System.getProperty("line.separator");
for (final Room room : map) sb.append(room.toString()).append(newline);
return sb.toString();
}
BTW I have benchmarked the call. The code:
public class GetProperty
{
static char[] ary = new char[1];
#GenerateMicroBenchmark public void everyTime() {
for (int i = 0; i < 100_000; i++) ary[0] = System.getProperty("line.separator").charAt(0);
}
#GenerateMicroBenchmark public void cache() {
final char c = System.getProperty("line.separator").charAt(0);
for (int i = 0; i < 100_000; i++) ary[0] = (char)(c | ary[0]);
}
}
The results:
Benchmark Mode Thr Cnt Sec Mean Mean error Units
GetProperty.cache thrpt 1 3 5 10.318 0.223 ops/msec
GetProperty.everyTime thrpt 1 3 5 0.055 0.000 ops/msec
The cached approach is more than two orders of magnitude faster.
Do note that the overall impact of getProperty call against all that string building is very, very unlikely to be noticeable.
You do not need to fear that the line separator will change while your code is running, so I see no reason against caching it.
Caching a value is certainly faster than executing a call over and over, but the difference will probably be negligible.
If you have become aware of a performance problem that you know relates to this, yes.
If you haven't, then no, the lookup is unlikely to have enough overhead to matter.
This would fall under either or both of the general categories "micro-optimization" and "premature optimization." :-)
But if you're worried about efficiency, you probably have a much bigger opportunity in that your toString method is regenerating the string every time. If toString will be called a lot, rather than caching the line terminator, cache the generated string, and clear that whenever your map of rooms changes. E.g.:
#Override
public String toString()
{
if (cachedString == null)
{
final StringBuilder sb = new StringBuilder();
final String ls = System.getProperty("line.separator");
for (final Room room : map)
{
sb.append(room.toString());
sb.append(ls);
}
cachedString = sb.toString();
}
return cachedString;
}
...and when your map changes, do
cachedString = null;
That's a lot more bang for the buck (the buck being the overhead of an extra field). Granted it's per-instance rather than per-class, so (reference earlier comment about efficiency) only do it if you have a good reason to.
Since it's so easy to do, why not? At the very least the implementation of System.getProperty() will have to do a hash table lookup (even if cached internally) to find the property you are requesting, then the virtual method getString() will be called on the resulting Object. None of these are very expensive but will need to be called multiple times. Not to mention many String temporaries will be created and need GCing after.
If you move this out to the top of your loop and reuse the same value, you avoid all of these problems. So why not?
If the system property is guaranteed to remain constant during the application it can be cached but in general you will loose the feature of the property which is changing the behavior when you change it.
For instance a text generator could use the property to generate text for windows or for linux and allow the property to be changed dynamically in the application, why not ?
In general, catching a property implies making useless the function setProperty.