I was doing code review and told someone to remove a private method that was only used once. They said that it didn't matter since dex count wouldn't get increased by private method references. Is this true? I wasn't able to find an answer with a simple google search.
The 64k limit is a limit on the number of unique method references in a dex file. A method reference consists of a specific class name, the method name and the method prototype, and is created when you either invoke a method or declare/define/override a method.
So yes, defining a new private method will add a method reference to the dex file.
For more information, see: https://source.android.com/devices/tech/dalvik/dex-format.html and https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html, which are the main references for the dex format.
The "method reference list" is a sorted list of method_id_items in the dex file. e.g. look for "method_ids" in the "File layout" section of dex-format.html. And further down the page, method_id_item is defined as consisting of a class reference, a method name and a method prototype.
The class_data_item section is used to define the methods and fields that are defined by the class. The "direct_methods" and "virtual_methods" lists are lists of indexes into the method_ids list - which requires that a reference for that method exists in the method_ids list.
And in dalvik-bytecode.html, the invoke-* instructions use a method index to refer the method to invoke.
Interestingly, the method reference list itself is defined with a 32-bit size value (search for "method_ids_size" in dex-format.html). So the list of method references itself can be as large as 4294967296 entries.
However, the problem comes when you need to reference any of these methods. The invoke-* instructions only use 16 bits to encode the method index.
Additionally, the method references in the class_data item can be up to the full 32 bits. So you could theoretically have definitions of methods past the 64k limit in a dex file, as long as you never actually tried to invoke them from within that dex file. But they could still be invoked from another dex file.
They said that it didn't matter since dex count wouldn't get increased by private method references. Is this true?
I am going to assume that you are worried about the 64K DEX method reference limit. In this case, the reviewee is correct: whether this code is wrapped in a method or not has no impact.
The "method reference" of relevance in "the 64K DEX method reference limit" refers to one DEX referring to a method in another DEX. In conventional Android projects, there are two DEXes of relevance: yours and the framework's. How many methods your own code is divided into does not matter. What matters is how many methods of the framework you are referencing (where by "you" I mean your code plus any library modules and JARs you are including).
When you enable multidex on a project, you are splitting your code into more than one DEX file. Each can refer to 64K methods in other DEX files. Here, though, "other DEX files" refers to both the framework DEX and the other DEX files of your own app created by multidex. However, AFAIK, multidex does not split a single class across DEX files. Since this is a private method, it could only be referenced by another method in the same class, and so both methods should be in the same DEX file. Hence, even in this case, having the private method would not add to the DEX method reference count of the DEX containing that method.
Based on JesusFreke's comments, I retract my original position. Defining a private method will increase the DEX method reference count.
That being said, on a one-off basis, worrying about inlining an individual method, just to reduce the DEX method reference count, is premature optimization IMHO. If you are bumping up towards the DEX method reference limit (and Android Studio's APK Analyzer can help you determine this), worry first about "trimming the fat" in libraries. Otherwise, worry about maintainability. Now, it may be that removing the method actually helps with that (e.g., it's a two-line method, not worth pulling out separately). But if there is maintainability value in having the method, leave it alone.
Related
I'm interested in understanding how a dex file (classesN.dex) references methods in another classesN.dex file.
In a standard dex layout, you have all of the class, method, type, etc... definitions in different tables. Things that are dynamically linked (such as those from the Android framework) simply have their method prototypes included, but no code data. Is it true that in a multidex setup, each classesN.dex contains a set of class implementations, and methods that are implemented in other dex files are merely included in the same way as dynamically linked calls?
In other words, if classes.dex needs to reference a method classes1.dex, it will include that method as a prototype within classes.dex, and then include its implementation in classes1.dex?
I ended up solving this question: it turns out that in a multidex layout the relevant method and class definitions are included in each dex file. For example, if classes.dex references methods foo() from classes1.dex, it will include a relevant entry in the method table for foo() within classes.dex's method table. But the implementation of foo() will appear in classes1.dex. This works because foo() is usually something like the entry of a library used by the app. The entry points of that library can be used without all of the methods called by foo. In classes.dex, foo will be defined without a corresponding code item, just as if it were a part of the dynamically linked Android standard library.
Sorry if this is too basic - I'm struggling to find Proguard documentation I can understand.
My understanding of how Proguard shrinks Android applications is by looking for unused (uncalled?) methods and eliminating them from the build. So if I have a method buynewCoke() that is never called anywhere else in the code, it will be removed.
However, what if there exists a method, say visitStoreAfterMidnight() that calls buyNewCoke(). And visitStoreAfterMidnight itself is never called. Does Proguard still remove both of these methods? Or does it keep buyNewCoke() because it is refrenced by something?
That is, if A calls B and nothing calls A, how does Proguard behave?
Both visitStoreAfterMidnight() and buyNewCoke() would be removed.
Actually it works similar to Garbage Collection, it starts from things that it needs to keep and check what they used and keep only these.
Edit:
official reference:
http://proguard.sourceforge.net/manual/introduction.html
Entry points
In order to determine which code has to be preserved and which code
can be discarded or obfuscated, you have to specify one or more entry
points to your code. These entry points are typically classes with
main methods, applets, midlets, activities, etc.
In the shrinking step, ProGuard starts from these seeds and
recursively determines which classes and class members are used. All
other classes and class members are discarded.
In dex code (e.g., as produced by the dexdump tool), for each method definition I see "ins" and "outs" in addition to other metadata such as "registers", "insns size".
I am instrumenting dex code to introduce new registers. The instrumentation is failing, and I suspect that I may have to change the "ins" and "outs" values based on the number of new registers I add.
So my question is: What do those "ins" and "outs" represent?
(fyi: I am using dexlib2 for this.)
These fields are documented at http://source.android.com/devices/tech/dalvik/dex-format.html.
ins_size | the number of words of incoming arguments to the method that this code is for
outs_size | the number of words of outgoing argument space required by this code for method invocation
ins_size is mostly self-explanatory - it's the number of 32-bit words required to store the method arguments (including the implicit "this" argument, for non-static methods). All arguments require 1 "word" except longs (J) and doubles (D), which require 2 words.
outs_size basically the opposite. outs_size must be set large enough to hold the arguments for any method call that occurs within the method.
If you want to instrument a dex file without having to worry about details like this, you might consider using dexlib2 (the library developed for and used by smali/baksmali to read/write dex files). The library is available in the maven repository, so it's easy to link against if you're using gradle/mvn.
I am working android application.when i run that application below error occred.please help me......
01-20 20:39:02.955: WARN/dalvikvm(5690): VFY: arbitrarily rejecting large method (regs=93 count=23019)
01-20 20:39:02.955: WARN/dalvikvm(5690): VFY: rejected Lez/com/Action_module_screen;.da ()V
01-20 20:39:02.955: WARN/dalvikvm(5690): Verifier rejected class Lez/com/Action_module_screen;
01-20 20:39:02.955: INFO/System.out(5690): verify Errorez.com.Action_module_screen
Some googling for causes and ways to fix:
From http://www.mentby.com/Group/android-developers/vfy-arbitrarily-rejecting-large-method.html
This means that the value of (number of registers * number of instruction words) is
larger than 2^21 [2,097,152].
Your error shows the method may have a lot of parameters and local variables (regs=93) and a large amount of code (instructions count=23019):
`93 registers * 23019 number of instruction words` = 2,140,767
exceeding the "size" limit of 2,097,152 by 43,615 (2%)
The verifier doesn't think your method is reasonable :-).
I wasn't really expecting anyone to hit this -- it's intended to
prevent the verifier from bloating up an app's native heap. Does the
method take a large number of arguments, or have lots of local
variables? I've also seen some poor behavior when a method had 4
invocations of a call to another method that took 15 arguments; the
register allocator freaked out a bit.
The presence or absence of debug information affects the sorts of
things the "dx" code optimizer is allowed to do (e.g. some unnecessary
instructions will be retained so that the debugger can show something
reasonable while single-stepping). Usually the overhead is small, but
it may be that you're near the edge and this pushed you over.
Also from http://www.mentby.com/Group/android-developers/verifyerror-arbitrarily-rejecting-large-method.html:
In addition to somehow reducing the width of parallel branching (eg,
by placing inner switch statements in their own methods), if you can
reduce the number of "global" local variables (method local variables
that reach all branch paths) that's likely to help substantially.
Yep, the Dalvik compiler attempts to assign a "register" to every
local variable in the method.
I don't favor this approach, but you could also convert some of your method parameters and local variables into instance variables:
By making them instance variables you remove
the compiler's need/desire to "manage" them (and also make the method
a fair amount smaller).
Here is a link to a similar question.
Your problem is a (93 * 23019) > 2^21 (2^21 = 2097152)
I believe ths short answer is: your method is too large, you need to optimize it to be smaller so that it will fit.
You have a method that is too large in your Errorez.com.Action_module_screen class ("arbitrarily rejecting large method"). It looks like the method in question is da(). Try reducing the size of that method.
it is normally showing that your apk contains code, that is not corresponding the OS version/ level or it has compilation problems. Sometimes it helps to clean the project and rebuiled everything.
Probably you should provide a longer stacktrace, if cleaning doesn't help :)
In the source code of Activity.java, I see some methods bellow :
public View findViewById(int id) {
return getWindow().findViewById(id);
}
and the definition of getWindow method:
public Window getWindow() {
return mWindow;
}
But as the following rules:
Avoid Internal Getters/Setters
In native languages like C++ it's
common practice to use getters (e.g. i
= getCount()) instead of accessing the field directly (i = mCount). This is
an excellent habit for C++, because
the compiler can usually inline the
access, and if you need to restrict or
debug field access you can add the
code at any time.
On Android, this is a bad idea.
Virtual method calls are expensive,
much more so than instance field
lookups. It's reasonable to follow
common object-oriented programming
practices and have getters and setters
in the public interface, but within a
class you should always access fields
directly.
Without a JIT, direct field access is
about 3x faster than invoking a
trivial getter. With the JIT (where
direct field access is as cheap as
accessing a local), direct field
access is about 7x faster than
invoking a trivial getter. This is
true in Froyo, but will improve in the
future when the JIT inlines getter
methods.
so I want to know why android developers not access this mWindow object directly? If the JIT of the current android versions cannot inline the access, getWindow().findViewById(id) will costs more time than mWindow.findViewById(id), and findViewById is a rather frequently used method.
First: you can't access it because it's private.
Why is it private?
As you said, accessing members directly is faster. On the other hand, you are invoking a method that isn't very fast as it will lookup for some view in the view hierarchy. So using a method instead of a direct access will incur in a small overhead in terms of percentage of the total time that it would take to perform that task.
Anyway, I believe that the reason for this is encapsulation.
You are invoking something you don't own (that is the Android SDK). So, you shouldn't make any assumptions of whats happening "in the other side". Simply use this method and expect that it will return the view you want (or null if it doesn't exists).
Maybe the next version of android will use a different method to lookup a view, not calling getWindow(). If you use this method, they (Google/Android) can simply mark the method as deprecated and "forward" your invocation to the newest implementation. If you were calling directly getWindow(), maybe you would be looking for something that is no longer placed in there.
You can't access the mWindow property directly - it's private.
And I wouldn't care about the speed of findViewById, since you only need to call it once for every view in your layout in your onCreate() method and store the views in members of your activity. You do call findViewById only once per view, don't you? ;-)
However, if you really care about these things, you could call getWindow() for yourself, store it into a local variable and call findViewById on it directly. I wouldn't recommend this because all your performance increasements here are not worth the time and anyway will be obsolete with future versions of the JIT.
If you do this I would be very interested in the amount of microseconds you saved. :-)
We have a reason to smile now...
The android documentation which says to avoid internal getters and setters will change soon, supposedly progruard was added to Gingerbread platform which does a fine job of inlining accessor's, please refer to "Avoid Internal Getters/Setters" is bad advice and these two SO posts.
https://stackoverflow.com/a/6716573/892055
https://stackoverflow.com/a/4930538/892055