我正在尝试将ClassFileTransformer
与Instrumentation API
一起使用。但是,在JVM
方法完成之前,我的premain
崩溃了。
我正在使用javassist
来操纵已加载类的字节码。
我的ClassFileTransformer
看起来像这样:
import java.io.File;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.nio.file.Files;
import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.Modifier;
public class JSGMain implements ClassFileTransformer {
public static void premain(String agentArguments, Instrumentation instrumentation) {
System.out.println("premain");
instrumentation.addTransformer(new JSGMain());
System.out.println("end premain");
}
public byte[] transform(
ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException
{
System.out.println("class="+className);
if (className == null) {
return classfileBuffer;
}
className = className.replace('/', '.');
// Check if class should be excluded / included
if (isExcluded(className) || !isIncluded(className)) {
return classfileBuffer;
}
// If the class object is loaded but an exception is thrown we must detach it in the finally block
CtClass classObj = null;
// If we do not make any modifications we return the original byte code
byte[] result = classfileBuffer;
try {
ClassPool pool = ClassPool.getDefault();
CtClass exceptionClass = pool.get("java.lang.Exception");
// Load class file
classObj = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
// Dont manipulate interfaces
if (!classObj.isInterface()) {
// Manipulate class methods
CtBehavior[] methods = classObj.getDeclaredBehaviors();
for (int i = 0; i < methods.length; i++) {
// Dont manipulate abstract or native methods
if (canTransformMethod(methods[i])) {
System.out.println("method="+methods[i].getName());
addMethodCallback(className, methods[i], exceptionClass);
}
}
// If class was modified we return the new byte code
result = classObj.toBytecode();
}
} catch (Exception e) {
System.err.println("className="+className);
e.printStackTrace();
} finally {
// We might not have modified the class at all
if (classObj != null) {
System.out.println("beforeDetach="+classObj.getName());
classObj.detach();
System.out.println("afterDetach="+classObj.getName());
}
}
// Return either original byte code or modified byte code
return result;
}
private boolean canTransformMethod(CtBehavior method) {
return !isAbstract(method) && !isNative(method);
}
private boolean isAbstract(CtBehavior method) {
return (method.getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT;
}
private boolean isNative(CtBehavior method) {
return (method.getModifiers() & Modifier.NATIVE) == Modifier.NATIVE;
}
private boolean isStatic(CtBehavior method) {
return (method.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
}
private void addMethodCallback(String className, CtBehavior method, CtClass exceptionClass) throws Exception {
String methodName = method.getName();
String beforeCallback;
String afterCallback;
if (method.getMethodInfo().isConstructor()) {
beforeCallback = getClass().getName()+".beforeConstructor(\""+className+"\");";
afterCallback = getClass().getName()+".afterConstructor(this);";
} else if (isStatic(method)) {
beforeCallback = getClass().getName()+".beforeStaticMethod(\""+className+"\", \""+methodName+"\");";
afterCallback = getClass().getName()+".afterStaticMethod(\""+className+"\", \""+methodName+"\");";
} else {
beforeCallback = getClass().getName()+".beforeMethod(this, \""+methodName+"\");";
afterCallback = getClass().getName()+".afterMethod(this, \""+methodName+"\");";
}
method.insertBefore(beforeCallback);
method.insertAfter(afterCallback);
String catchCallback = "{"+getClass().getName()+".exception(e); throw e;}";
method.addCatch(catchCallback, exceptionClass, "e");
}
}
它适用于大多数程序,但当我尝试在AWT / Swing application
我的JVM
崩溃时使用此类加载器时,使用以下日志:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000005e592f19, pid=2456, tid=5044
#
# JRE version: Java(TM) SE Runtime Environment (8.0_05-b13) (build 1.8.0_05-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.5-b02 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# V [jvm.dll+0x132f19]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
# http://bugreport.sun.com/bugreport/crash.jsp
#
(日志对我来说太长了) 在日志中,这条线引起了我的注意:
Event: 3.592 Executing VM operation: ParallelGCFailedAllocation
这可能是我的错误的原因,但我不知道如何解释它或如何解决它。
这是我启动程序的方式:
java -Xbootclasspath/p:JSG.jar -javaagent:JSG.jar -jar App.jar
感谢您的时间和帮助。
答案 0 :(得分:0)
我发现了问题所在:我的内存不足。
premain方法确实结束了,但在打印文本之前,JVM已经崩溃了。
事实证明,AWT和Swing调用了一些方法,我正在对所调用的任何方法进行一些操作,而这些操作对系统来说太过分了。 为什么它没有因为OutOfMemoryException而失败,我不知道。