[Memory-opti:static allocations] Reduce allocations and speedup ClassHeirarchyManager (#39)

This commit is contained in:
Alexdoru authored and GitHub committed 2025-12-05 22:21:23 +01:00
1 parent bb98bdc6d4
commit bb008a1265
3 files changed
+136 -105

No files matched your search

+9 -1
View File
@@ -90,7 +90,9 @@ usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
mixinsPackage =
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -166,6 +168,12 @@ curseForgeRelations =
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.48'
}
@@ -1,13 +1,12 @@
package codechicken.lib.asm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
@@ -18,43 +17,60 @@ import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
*/
public class ClassHeirachyManager implements IClassTransformer {
public static class SuperCache {
private static final Map<String, ClassInfo> superclasses = new ConcurrentHashMap<>(1000);
String superclass;
public HashSet<String> parents = new HashSet<String>();
private boolean flattened;
static {
superclasses.put("java.lang.Object", ClassInfo.OBJECT);
}
public void add(String parent) {
parents.add(parent);
@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (bytes == null) return null;
if (!superclasses.containsKey(transformedName)) {
declareASM(transformedName, bytes);
}
return bytes;
}
public void flatten() {
if (flattened) return;
private static ClassInfo declareASM(String name, byte[] bytes) {
final ClassNode node = ASMHelper.createClassNode(bytes, ClassReader.SKIP_CODE);
final ClassInfo classInfo = new ClassInfo(node);
superclasses.put(name, classInfo);
return classInfo;
}
for (String s : new ArrayList<String>(parents)) {
SuperCache c = declareClass(s);
if (c != null) {
c.flatten();
parents.addAll(c.parents);
}
private static ClassInfo declareReflection(String name) throws ClassNotFoundException {
final Class<?> aclass = Class.forName(name);
final ClassInfo classInfo = new ClassInfo(aclass);
superclasses.put(name, classInfo);
return classInfo;
}
private static ClassInfo declareClass(String name) {
try {
byte[] bytes = Launch.classLoader.getClassBytes(unKey(name));
if (bytes != null) {
return declareASM(name, bytes);
}
flattened = true;
} catch (Exception ignored) {}
try {
return declareReflection(name);
} catch (ClassNotFoundException ignored) {}
return null;
}
private static String toKey(String name) {
if (ObfMapping.obfuscated) {
return FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
}
return name.replace('/', '.');
}
public static HashMap<String, SuperCache> superclasses = new HashMap<String, SuperCache>();
private static LaunchClassLoader cl = Launch.classLoader;
public static String toKey(String name) {
if (ObfMapping.obfuscated)
name = FMLDeobfuscatingRemapper.INSTANCE.map(name.replace('.', '/')).replace('/', '.');
return name;
}
public static String unKey(String name) {
if (ObfMapping.obfuscated)
name = FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
return name;
private static String unKey(String name) {
if (ObfMapping.obfuscated) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(name.replace('.', '/')).replace('/', '.');
}
return name.replace('/', '.');
}
/**
@@ -68,83 +84,90 @@ public class ClassHeirachyManager implements IClassTransformer {
if (name.equals(superclass)) return true;
SuperCache cache = declareClass(name);
if (cache == null) // just can't handle this
return false;
ClassInfo classInfo = superclasses.get(name);
if (classInfo == null) classInfo = declareClass(name);
if (classInfo == null) return false;
cache.flatten();
return cache.parents.contains(superclass);
}
private static SuperCache declareClass(String name) {
name = toKey(name);
SuperCache cache = superclasses.get(name);
if (cache != null) return cache;
try {
byte[] bytes = cl.getClassBytes(unKey(name));
if (bytes != null) cache = declareASM(bytes);
} catch (Exception e) {}
if (cache != null) return cache;
try {
cache = declareReflection(name);
} catch (ClassNotFoundException e) {}
return cache;
}
private static SuperCache declareReflection(String name) throws ClassNotFoundException {
Class<?> aclass = Class.forName(name);
SuperCache cache = getOrCreateCache(name);
if (aclass.isInterface()) cache.superclass = "java.lang.Object";
else if (name.equals("java.lang.Object")) return cache;
else cache.superclass = toKey(aclass.getSuperclass().getName());
cache.add(cache.superclass);
for (Class<?> iclass : aclass.getInterfaces()) cache.add(toKey(iclass.getName()));
return cache;
}
private static SuperCache declareASM(byte[] bytes) {
ClassNode node = ASMHelper.createClassNode(bytes);
String name = toKey(node.name);
SuperCache cache = getOrCreateCache(name);
cache.superclass = toKey(node.superName.replace('/', '.'));
cache.add(cache.superclass);
for (String iclass : node.interfaces) cache.add(toKey(iclass.replace('/', '.')));
return cache;
}
@Override
public byte[] transform(String name, String tname, byte[] bytes) {
if (bytes == null) return null;
if (!superclasses.containsKey(tname)) declareASM(bytes);
return bytes;
}
public static SuperCache getOrCreateCache(String name) {
SuperCache cache = superclasses.get(name);
if (cache == null) superclasses.put(name, cache = new SuperCache());
return cache;
return classInfo.hasSuper(superclass);
}
public static String getSuperClass(String name, boolean runtime) {
name = toKey(name);
SuperCache cache = declareClass(name);
if (cache == null) return "java.lang.Object";
cache.flatten();
String s = cache.superclass;
if (!runtime) s = FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
ClassInfo classInfo = superclasses.get(name);
if (classInfo == null) classInfo = declareClass(name);
if (classInfo == null || classInfo.superclass == null) return "java.lang.Object";
final String s = classInfo.superclass;
if (!runtime) {
return FMLDeobfuscatingRemapper.INSTANCE.unmap(s);
}
return s;
}
private static class ClassInfo {
public static final ClassInfo OBJECT = new ClassInfo();
public final String superclass;
public final String[] interfaces;
public ClassInfo parent;
private ClassInfo() {
this.superclass = null;
this.interfaces = null;
}
public ClassInfo(ClassNode node) {
if ("java/lang/Object".equals(node.superName)) {
superclass = "java.lang.Object";
parent = OBJECT;
} else {
superclass = toKey(node.superName);
}
if (node.interfaces.isEmpty()) {
interfaces = null;
} else {
interfaces = new String[node.interfaces.size()];
for (int i = 0; i < node.interfaces.size(); i++) {
interfaces[i] = toKey(node.interfaces.get(i));
}
}
}
public ClassInfo(Class<?> aclass) {
if (aclass.isInterface()) {
this.superclass = "java.lang.Object";
this.parent = OBJECT;
} else {
final Class<?> superclass = aclass.getSuperclass();
if (superclass == null || "java.lang.Object".equals(superclass.getName())) {
this.superclass = "java.lang.Object";
this.parent = OBJECT;
} else {
this.superclass = toKey(superclass.getName());
}
}
final Class<?>[] interfaces = aclass.getInterfaces();
if (interfaces.length == 0) {
this.interfaces = null;
} else {
this.interfaces = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
this.interfaces[i] = toKey(interfaces[i].getName());
}
}
}
public boolean hasSuper(String superclass) {
if (this.superclass == null) return false;
if (this.superclass.equals(superclass)) return true;
ClassInfo parentInfo = this.parent;
if (parentInfo == null) parentInfo = superclasses.get(this.superclass);
if (parentInfo == null) parentInfo = declareClass(this.superclass);
if (parentInfo == null) return false;
this.parent = parentInfo;
return parentInfo.hasSuper(superclass);
}
}
}