The code below calls two simple functions 10 billion times each.
public class PerfTest {
    private static long l = 0;
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b");
        long time1 = System.currentTimeMillis();
        for (long i = 0; i < 1E10; i++) {
            func1("a", "b");
        }
        long time2 = System.currentTimeMillis();
        for (long i = 0; i < 1E10; i++) {
            func2(list);
        }
        System.out.println((time2 - time1) + "/" + (System.currentTimeMillis() - time2));
    }
    private static void func1(String s1, String s2) { l++; }
    private static void func2(List<String> sl) { l++; }
}
My assumption was that the performance of these two calls would be close to identical. If anything I would have guessed that passing two arguments would be slightly slower than passing one. Given all arguments are object references I wasn't expecting the fact that one was a list to make any difference.
I have run the test many times and a typical result is "12781/30536". In other words, the call using two strings takes 13 secs and the call using a list takes 30 secs.
What is the explanation for this difference in performance? Or is this an unfair test? I have tried switching the two calls (in case it was due to startup effects) but the results are the same.
Update
This is not a fair test for many reasons. However it does demonstrate real behaviour of the Java compiler. Note the following two additions to demonstrate this:
- Adding expressions s1.getClass()andsl.getClass()to the functions makes the two function calls perfom the same
- Running the test with  -XX:-TieredCompilationalso makes the two functions calls perform the same
The explanation for this behaviour is in the accepted answer below. The very brief summary of @apangin's answer is that func2 is not inlined by the hotspot compiler because the class of its argument (i.e. List) is not resolved. Forcing resolution of the class (e.g. using getClass) causes it to be inlined which significantly improves its performance. As pointed out in the answer, unresolved classes are unlikely to occur in real code which makes this code a unrealistic edge case.
 
     
    