简体   繁体   中英

JVM crashes with EXCEPTION_ACCESS_VIOLATION when using Instrumentation API

I am trying to use a ClassFileTransformer with the Instrumentation API . However, my JVM crashes before my premain method finishes.

I am using javassist to manipulate the byte-code of loaded classes.

My ClassFileTransformer looks like this:

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");
    }
}

It works for most programs, but when I try to use this class loader on an AWT / Swing application my JVM crashes with the following log:

#
# 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
#

(The log is too long for me to post) Of the log, this line grabbed my attention:

Event: 3.592 Executing VM operation: ParallelGCFailedAllocation

This might be the cause for my error, but I dont know how to interprete it or how to fix it.

This is the way I am starting my program:

java -Xbootclasspath/p:JSG.jar -javaagent:JSG.jar -jar App.jar

Thanks for your time and help.

I found out what the problem was: I was running out of memory.

The premain method did end, but before the text was printed the JVM crashed already.

It turns out AWT and Swing call buttloads of methods, I was doing some operations on any method being called, and those were simply too much for the system. Why it didnt fail with an OutOfMemoryException instead is not known to me though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM