JDK-6580709 : One-method interfaces on one-interface classes are special
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 7,9,10
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2007-07-14
  • Updated: 2018-10-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Description
Here is a quick look at classes in rt.jar shows how (statically) common are definitions and uses of few-interface classes in the java core:

number of methods, interface definitions (rt.jar under java package)
328	(total)
105	(32.0%)	=32.0%	1 methods
42	(12.8%)	=44.8%	2 methods
28	(8.5%)	=53.4%	0 methods
28	(8.5%)	=61.9%	3 methods
25	(7.6%)	=69.5%	(other, frequency less than 5)
22	(6.7%)	=76.2%	5 methods
18	(5.5%)	=81.7%	4 methods
16	(4.9%)	=86.6%	6 methods
11	(3.4%)	=89.9%	7 methods
8	(2.4%)	=92.4%	8 methods
7	(2.1%)	=94.5%	9 methods
7	(2.1%)	=96.6%	15 methods
6	(1.8%)	=98.5%	10 methods
5	(1.5%)	=100.0%	11 methods

number of methods, interface method references (rt.jar under java package)
2127	(total)
303	(14.2%)	=14.2%	3 methods (all interfaces)
261	(12.3%)	=26.5%	14 methods in java.util.Map
234	(11.0%)	=37.5%	(other, frequency less than 20)
189	(8.9%)	=46.4%	5 methods (all interfaces)
184	(8.7%)	=55.1%	25 methods in java.util.List
126	(5.9%)	=61.0%	15 methods in java.util.Collection
125	(5.9%)	=66.9%	2 methods (all interfaces)
122	(5.7%)	=72.6%	1 methods (all interfaces)
122	(5.7%)	=78.3%	15 methods in java.util.Set
77	(3.6%)	=81.9%	4 methods (all interfaces)
51	(2.4%)	=84.3%	9 methods in java.text.AttributedCharacterIterator
49	(2.3%)	=86.6%	10 methods in java.util.concurrent.ConcurrentNavigableMap
48	(2.3%)	=88.9%	9 methods in java.util.ListIterator
43	(2.0%)	=90.9%	46 methods in java.awt.peer.ComponentPeer
42	(2.0%)	=92.9%	21 methods in java.util.NavigableMap
33	(1.6%)	=94.5%	7 methods in java.awt.image.ImageConsumer
28	(1.3%)	=95.8%	10 methods in java.text.CharacterIterator
24	(1.1%)	=96.9%	7 methods in java.util.concurrent.locks.Condition
23	(1.1%)	=98.0%	6 methods in java.util.SortedSet
23	(1.1%)	=99.1%	9 methods in java.util.SortedMap
20	(0.9%)	=100.0%	11 methods in java.util.concurrent.BlockingQueue

frequently mentioned interface methods (by inspection of constant pools)
2127	(total)
1385	(65.1%)	=65.1%	(other, frequency less than 20)
117	(5.5%)	=70.6%	hasNext()Z in java.util.Iterator (3 methods)
117	(5.5%)	=76.1%	next()Ljava/lang/Object; in java.util.Iterator (3 methods)
61	(2.9%)	=79.0%	get(Ljava/lang/Object;)Ljava/lang/Object; in java.util.Map
51	(2.4%)	=81.4%	iterator()Ljava/util/Iterator; in java.util.Set
51	(2.4%)	=83.8%	put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; in java.util.Map
49	(2.3%)	=86.1%	getKey()Ljava/lang/Object; in java.util.Map$Entry (5 methods)
43	(2.0%)	=88.1%	getValue()Ljava/lang/Object; in java.util.Map$Entry (5 methods)
35	(1.6%)	=89.8%	iterator()Ljava/util/Iterator; in java.util.Collection
34	(1.6%)	=91.3%	add(Ljava/lang/Object;)Z in java.util.List
33	(1.6%)	=92.9%	hasMoreElements()Z in java.util.Enumeration (2 methods)
32	(1.5%)	=94.4%	nextElement()Ljava/lang/Object; in java.util.Enumeration (2 methods)
26	(1.2%)	=95.6%	size()I in java.util.List
26	(1.2%)	=96.9%	size()I in java.util.Map
23	(1.1%)	=97.9%	remove()V in java.util.Iterator (3 methods)
22	(1.0%)	=99.0%	entrySet()Ljava/util/Set; in java.util.Map
22	(1.0%)	=100.0%	get(I)Ljava/lang/Object; in java.util.List
It is very common for a class to implement a single interface with a single method.  An anonymous instance of Runnable is a key example.  All closure types (if Java were to get closures) would also be examples.  We should optimize this case.

(See next description section for some static counts which illustrate the frequency of few-method interfaces.  It appears that about 1/4 of all interface method references are to interfaces with 1, 2, or 3 methods.)

In what follows, we can disregard the handful of methods on Object and any interfaces with no methods at all.

The key insight is that if we are calling a method on such an interface, and if the object's class truly does implement just that one interface, there is only one possible entry point, so we might as well just put that entry point into the class's vtable immediately after the Object vtable prefix (hashCode, etc.).

In order for this to be safe, we need a type check.  The type check is simple and requires no additional memory references.  Have the caller pass the intended interface in a side register (as monomorphic inline caches do already) and have the callee's entry point check this register against the expected interface.  If the check fails, back off to itables and error processing.

In addition, the vtable slot used for one-method interfaces cannot be used for any other purpose, lest a caller use this calling sequence on an unprepared object.

This is very much like the fast execution path for monomorphic inline caches, with two key differences:  1. the branch is indirect through the vtable, and 2. the entry point check is against the receiver's sole interface, not against his concrete type.

In the case of an object which implements two one-method interfaces, the vtable slot must be filled with an adapter that does the itable check (based on the interface in the side-register) and branches to the correct method.  Any class which implements no such interfaces must have an error-signalling routine in this vtable slot.  These are uncommon cases.

The common case is that invokeinterface will use no more memory references than invokevirtual.  Here is the code trace:
	callsite:  // IFC x; x.foo()
	mov #IFC, %side
	ld [%recv+4], %temp
	ld [%temp, #VTBASE], %temp2
	call %temp2
	----
	foo_IEP:  // IFC.foo, interface entry point
	cmp %side, #IFC
	bne Slow_Path
	// foo starts here

This technique applies to 2- or 3-method interfaces, such as Iterator.  Two or three (not one) vtable entries would be reserved.  Which slot would be which?  The order of declaration within each N-method interface would be a sufficient guide.  Four-method interfaces are also possible, but it seems like diminishing returns, given that every class must pay for this technique with extra vtable slots.

This technique would be complementary to our other fast paths.  It would apply to many places where we currently resort to itables, because the call site is polymorphic.

Comments
JDK 8 Project Lambda uses single-method interfaces to represent closures. Thus, such interfaces are becoming more and more common, and also more polymorphic. This RFE is no cure-all, since the best optimization of a closure (by far) is to inline it. But when megamorphic call sites defeat inlining, tuning their call paths can reduce the downside.
17-03-2014