4848import static org .objectweb .asm .Opcodes .ACC_VARARGS ;
4949
5050import java .io .IOException ;
51+ import java .lang .ref .WeakReference ;
5152import java .lang .reflect .Constructor ;
5253import java .lang .reflect .InvocationTargetException ;
5354import java .lang .reflect .Method ;
5455import java .lang .reflect .Modifier ;
5556import java .util .*;
57+ import java .util .concurrent .atomic .AtomicReferenceFieldUpdater ;
5658import java .util .stream .Collectors ;
5759
5860import org .jruby .anno .JRubyClass ;
9395import org .jruby .util .Loader ;
9496import org .jruby .util .OneShotClassLoader ;
9597import org .jruby .util .StringSupport ;
96- import org .jruby .util .WeakIdentityHashMap ;
9798import org .jruby .util .log .Logger ;
9899import org .jruby .util .log .LoggerFactory ;
99100import org .objectweb .asm .AnnotationVisitor ;
@@ -1060,8 +1061,8 @@ public final Collection<RubyClass> subclasses() {
10601061 }
10611062
10621063 public Collection <RubyClass > subclasses (boolean includeDescendants ) {
1063- Map < RubyClass , Object > subclasses = this .subclasses ;
1064- if (subclasses != null ) {
1064+ SubclassNode subclassNode = this .subclassNode ;
1065+ if (subclassNode != null ) {
10651066 Collection <RubyClass > mine = new ArrayList <>();
10661067 subclassesInner (mine , includeDescendants );
10671068
@@ -1071,30 +1072,94 @@ public Collection<RubyClass> subclasses(boolean includeDescendants) {
10711072 }
10721073
10731074 private void subclassesInner (Collection <RubyClass > mine , boolean includeDescendants ) {
1074- Map <RubyClass , Object > subclasses = this .subclasses ;
1075- if (subclasses != null ) {
1076- Set <RubyClass > keys = subclasses .keySet ();
1077- mine .addAll (keys );
1078- if (includeDescendants ) {
1079- for (RubyClass klass : keys ) {
1080- klass .subclassesInner (mine , includeDescendants );
1075+ SubclassNode subclassNode = this .subclassNode ;
1076+ if (subclassNode != null ) {
1077+ int clearedCount = 0 ;
1078+ while (subclassNode != null ) {
1079+ RubyClass klass = subclassNode .ref .get ();
1080+ subclassNode = subclassNode .next ;
1081+
1082+ if (klass == null ) {
1083+ clearedCount ++;
1084+ continue ;
10811085 }
1086+
1087+ mine .add (klass );
1088+
1089+ if (includeDescendants ) klass .subclassesInner (mine , includeDescendants );
1090+ }
1091+ int newSize = mine .size ();
1092+
1093+ // tidy up if more than 25% cleared references
1094+ if ((double ) clearedCount / newSize > 0.25 ) {
1095+ cleanSubclasses ();
10821096 }
10831097 }
10841098 }
10851099
10861100 private void concreteSubclasses (RubyArray <RubyClass > subs ) {
1087- Map <RubyClass , Object > subclasses = this .subclasses ;
1088- if (subclasses != null ) {
1089- subclasses .forEach ((klass , $ ) -> {
1101+ SubclassNode subclassNode = this .subclassNode ;
1102+ if (subclassNode != null ) {
1103+ int clearedCount = 0 ;
1104+ while (subclassNode != null ) {
1105+ RubyClass klass = subclassNode .ref .get ();
1106+ subclassNode = subclassNode .next ;
1107+
1108+ if (klass == null ) {
1109+ clearedCount ++;
1110+ continue ;
1111+ }
1112+
10901113 if (!klass .isSingleton ()) {
10911114 if (klass .isIncluded () || klass .isPrepended ()) {
10921115 klass .concreteSubclasses (subs );
10931116 } else {
10941117 subs .append (klass );
10951118 }
10961119 }
1097- });
1120+ }
1121+ int newSize = subs .size ();
1122+ subclassEstimate = newSize + 4 ;
1123+
1124+ // tidy up if more than 25% cleared references
1125+ if ((double ) clearedCount / newSize > 0.25 ) {
1126+ cleanSubclasses ();
1127+ }
1128+ }
1129+ }
1130+
1131+ private void cleanSubclasses () {
1132+ SubclassNode subclassNode = this .subclassNode ;
1133+ SubclassNode newTop = rebuildSubclasses (subclassNode );
1134+ while (!SUBCLASS_UPDATER .compareAndSet (this , subclassNode , newTop )) {
1135+ subclassNode = this .subclassNode ;
1136+ newTop = rebuildSubclasses (subclassNode );
1137+ }
1138+ }
1139+
1140+ private static SubclassNode rebuildSubclasses (SubclassNode subclassNode ) {
1141+ SubclassNode newTop = null ;
1142+ while (subclassNode != null ) {
1143+ WeakReference <RubyClass > ref = subclassNode .ref ;
1144+ RubyClass klass = ref .get ();
1145+ subclassNode = subclassNode .next ;
1146+ if (klass == null ) continue ;
1147+ newTop = new SubclassNode (ref , newTop );
1148+ }
1149+ return newTop ;
1150+ }
1151+
1152+ // TODO: make into a Record
1153+ static class SubclassNode {
1154+ final SubclassNode next ;
1155+ final WeakReference <RubyClass > ref ;
1156+ SubclassNode (RubyClass klass , SubclassNode next ) {
1157+ ref = new WeakReference <>(klass );
1158+ this .next = next ;
1159+ }
1160+ SubclassNode (WeakReference <RubyClass > ref , SubclassNode next ) {
1161+ this .ref = ref ;
1162+ this .next = next ;
10981163 }
10991164 }
11001165
@@ -1108,18 +1173,12 @@ private void concreteSubclasses(RubyArray<RubyClass> subs) {
11081173 * @param subclass The subclass to add
11091174 */
11101175 public void addSubclass (RubyClass subclass ) {
1111- Map <RubyClass , Object > subclasses = this .subclasses ;
1112- if (subclasses == null ) {
1113- // check again
1114- synchronized (this ) {
1115- subclasses = this .subclasses ;
1116- if (subclasses == null ) {
1117- this .subclasses = subclasses = Collections .synchronizedMap (new WeakHashMap <>(4 ));
1118- }
1119- }
1176+ SubclassNode subclassNode = this .subclassNode ;
1177+ SubclassNode newNode = new SubclassNode (subclass , subclassNode );
1178+ while (!SUBCLASS_UPDATER .compareAndSet (this , subclassNode , newNode )) {
1179+ subclassNode = this .subclassNode ;
1180+ newNode = new SubclassNode (subclass , subclassNode );
11201181 }
1121-
1122- subclasses .put (subclass , NEVER );
11231182 }
11241183
11251184 /**
@@ -1128,10 +1187,16 @@ public void addSubclass(RubyClass subclass) {
11281187 * @param subclass The subclass to remove
11291188 */
11301189 public void removeSubclass (RubyClass subclass ) {
1131- Map <RubyClass , Object > subclasses = this .subclasses ;
1132- if (subclasses == null ) return ;
1133-
1134- subclasses .remove (subclass );
1190+ SubclassNode subclassNode = this .subclassNode ;
1191+ while (subclassNode != null ) {
1192+ WeakReference <RubyClass > ref = subclassNode .ref ;
1193+ RubyClass klass = ref .get ();
1194+ if (klass == subclass ) {
1195+ ref .clear ();
1196+ return ;
1197+ }
1198+ subclassNode = subclassNode .next ;
1199+ }
11351200 }
11361201
11371202 /**
@@ -1141,20 +1206,25 @@ public void removeSubclass(RubyClass subclass) {
11411206 * @param newSubclass The subclass to replace it with
11421207 */
11431208 public void replaceSubclass (RubyClass subclass , RubyClass newSubclass ) {
1144- Map <RubyClass , Object > subclasses = this .subclasses ;
1145- if (subclasses == null ) return ;
1146-
1147- subclasses .remove (subclass );
1148- subclasses .put (newSubclass , NEVER );
1209+ removeSubclass (subclass );
1210+ addSubclass (newSubclass );
11491211 }
11501212
1213+ /**
1214+ * make this class and all subclasses sync
1215+ */
11511216 @ Override
11521217 public void becomeSynchronized () {
1153- // make this class and all subclasses sync
11541218 super .becomeSynchronized ();
1155- Map <RubyClass , Object > subclasses = this .subclasses ;
1156- if (subclasses != null ) {
1157- for (RubyClass subclass : subclasses .keySet ()) subclass .becomeSynchronized ();
1219+
1220+ SubclassNode subclassNode = this .subclassNode ;
1221+ while (subclassNode != null ) {
1222+ WeakReference <RubyClass > ref = subclassNode .ref ;
1223+ RubyClass klass = ref .get ();
1224+ if (klass != null ) {
1225+ klass .becomeSynchronized ();
1226+ }
1227+ subclassNode = subclassNode .next ;
11581228 }
11591229 }
11601230
@@ -1174,9 +1244,14 @@ public void becomeSynchronized() {
11741244 public void invalidateCacheDescendants () {
11751245 super .invalidateCacheDescendants ();
11761246
1177- Map <RubyClass , Object > subclasses = this .subclasses ;
1178- if (subclasses != null ) {
1179- for (RubyClass subclass : subclasses .keySet ()) subclass .invalidateCacheDescendants ();
1247+ SubclassNode subclassNode = this .subclassNode ;
1248+ while (subclassNode != null ) {
1249+ WeakReference <RubyClass > ref = subclassNode .ref ;
1250+ RubyClass klass = ref .get ();
1251+ if (klass != null ) {
1252+ klass .invalidateCacheDescendants ();
1253+ }
1254+ subclassNode = subclassNode .next ;
11801255 }
11811256 }
11821257
@@ -1187,12 +1262,15 @@ public void addInvalidatorsAndFlush(List<Invalidator> invalidators) {
11871262 // if we're not at boot time, don't bother fully clearing caches
11881263 if (!runtime .isBootingCore ()) cachedMethods .clear ();
11891264
1190- Map <RubyClass , Object > subclasses = this .subclasses ;
1191- // no subclasses, don't bother with lock and iteration
1192- if (subclasses == null || subclasses .isEmpty ()) return ;
1193-
1194- // cascade into subclasses
1195- for (RubyClass subclass : subclasses .keySet ()) subclass .addInvalidatorsAndFlush (invalidators );
1265+ SubclassNode subclassNode = this .subclassNode ;
1266+ while (subclassNode != null ) {
1267+ WeakReference <RubyClass > ref = subclassNode .ref ;
1268+ RubyClass klass = ref .get ();
1269+ if (klass != null ) {
1270+ klass .addInvalidatorsAndFlush (invalidators );
1271+ }
1272+ subclassNode = subclassNode .next ;
1273+ }
11961274 }
11971275
11981276 public final Ruby getClassRuntime () {
@@ -3087,7 +3165,8 @@ public IRubyObject invokeFrom(ThreadContext context, CallType callType, IRubyObj
30873165 protected final Ruby runtime ;
30883166 private ObjectAllocator allocator ; // the default allocator
30893167 protected ObjectMarshal marshal ;
3090- private volatile Map <RubyClass , Object > subclasses ;
3168+ private volatile SubclassNode subclassNode ;
3169+ private static final AtomicReferenceFieldUpdater SUBCLASS_UPDATER = AtomicReferenceFieldUpdater .newUpdater (RubyClass .class , SubclassNode .class , "subclassNode" );
30913170 private int subclassEstimate = -1 ;
30923171 public static final int CS_IDX_INITIALIZE = 0 ;
30933172 public enum CS_NAMES {
0 commit comments