java.lang. Instrument 에이전트 사용 상세 정보

7690 단어 javaInstrumentAgent
java.lang. Instrument 에이전트 사용
java.lang. Instrument 패키지는 JDK5에서 도입된 것으로 프로그래머는 수정 방법의 바이트 코드를 통해 동적 수정 클래스 코드를 실현한다.이것은 보통 클래스의main 방법이 호출되기 전에 미리 처리하는 작업이며,java를 통해 이 클래스의 에이전트 클래스를 지정합니다.클래스의 바이트 코드가 JVM에 불러오기 전에 ClassFile Transformer의transform 방법을 호출하여 원본 방법을 수정하는 기능을 실현하고 AOP를 실현한다. 이 장점은 동적 에이전트나 CGLIB 기술이 AOP를 실현하는 것처럼 새로운 클래스가 생기지 않고 원본에 인터페이스가 있어야 한다는 것이다.
(1) 에이전트 (에이전트) 는 당신의main 방법 앞에 있는 차단기 (interceptor), 즉main 방법이 실행되기 전에 에이전트의 코드를 실행합니다.에이전트의 코드는 당신의main 방법과 같은 JVM에서 실행되고 같은 시스템classloader에 불러옵니다. 같은 보안 정책(security policy)과 상하문(context)에 의해 관리됩니다.에이전트(agent)라는 이름은 우리가 일반적으로 이해하는 에이전트와 크게 다르지 않다.자바 에이전트는 사용하기에 비교적 간단하다.어떻게 자바 에이전트를 씁니까?premain만 실현하는 방법: public static void premain(String agent Args, Instrumentation inst) JDK 6에서 위의 premain 정의를 찾지 못하면 아래의 premain 정의를 호출하려고 시도합니다: public static void premain(String agent Args)
(2) Agent 클래스는 반드시 jar 패키지로 묶고 그 안에 있는 META-INF/MAINIFEST로 묶어야 한다.MF, Premain-Class 속성이 포함되어야 합니다.다음은 마니페스트입니다.MF의 예:
Manifest-Version: 1.0 Premain-Class:MyAgent1 Created-By:1.6.0_06
그리고 마니페스트를.MF를 당신의jar가방에 추가합니다.다음은 에이전트jar 파일의 Manifest Attributes 목록입니다. Premain-Class JVM이 시작될 때 프록시를 지정하면 이 속성은 프록시 클래스, 즉premain 방법을 포함하는 클래스를 지정합니다.JVM이 시작할 때 프록시를 지정한 경우 이 속성은 필수입니다.속성이 없으면 JVM이 중단됩니다.주: 이 속성은 클래스 이름이지 파일 이름이나 경로가 아닙니다.Agent-Class VM 부팅 후 어느 시점에 에이전트를 시작하는 메커니즘을 지원하면 이 속성은 에이전트 클래스를 지정합니다.에이전트main 방법을 포함하는 클래스입니다.이 속성은 필수입니다. 존재하지 않으면 에이전트를 시작할 수 없습니다.주: 이것은 파일 이름이나 경로가 아니라 클래스 이름입니다.Boot-Class-Path 부트 클래스 로더가 검색하는 경로 목록을 설정합니다.경로는 디렉토리 또는 라이브러리를 나타냅니다(여러 플랫폼에서 일반적으로 JAR 또는 zip 라이브러리로 참조됨).클래스를 찾는 플랫폼에 특정한 메커니즘이 실패하면 가이드 클래스 캐리어가 이 경로를 검색합니다.나열된 순서대로 경로를 검색합니다.목록의 경로는 하나 이상의 공백으로 구분됩니다.경로는 계층형 URI의 경로 구성 요소 구문을 사용합니다.경로가 슬래시 문자 ("/") 로 시작하는 경우 절대 경로이고 그렇지 않으면 상대 경로입니다.상대 경로는 에이전트 JAR 파일의 절대 경로에 따라 해석됩니다.형식이 올바르지 않은 경로와 존재하지 않는 경로를 무시합니다.프록시가 VM이 시작된 후 어느 시점에 시작된 경우 JAR 파일을 나타내지 않는 경로는 무시됩니다.이 속성은 선택 사항입니다.Can-Redefine-Classes 부울 값(true 또는false, 대소문자와 무관).이 에이전트에 필요한 클래스를 다시 정의할 수 있습니까?true 이외의 값은 모두false로 간주됩니다.이 속성은 선택할 수 있습니다. 기본값은false입니다.Can-Retransform-Classes 부울 값(true 또는false, 대소문자와 무관).이 에이전트에 필요한 클래스를 다시 변환할 수 있습니까?true 이외의 값은 모두false로 간주됩니다.이 속성은 선택할 수 있습니다. 기본값은false입니다.Can-Set-Native-Method-Prefix 부울 값(true 또는false, 대소문자와 무관).이 에이전트에 필요한 기본 방법 접두사를 설정할 수 있습니까?true 이외의 값은 모두false로 간주됩니다.이 속성은 선택할 수 있습니다. 기본값은false입니다.
(3) 이 모든 에이전트의jar 패키지는 프로그램의classpath에 자동으로 추가됩니다.그래서 수동으로classpath에 추가할 필요가 없습니다.classpath의 순서를 지정하고 싶지 않으면
(4) 자바 프로그램에서 -javaagent라는 매개 변수의 개수는 제한이 없기 때문에 임의의 자바 에이전트를 추가할 수 있습니다.모든 자바 에이전트는 당신이 정의한 순서에 따라 실행됩니다.예:
java -javaagent:MyAgent1.jar -javaagent:MyAgent2.jar -jar MyProgram.jar
내 프로그램을 가정해 보자.jar의 main 함수는 MyProgram에 있습니다.MyAgent1.jar, MyAgent2.jar, 이 두 개의 jar 패키지에서premain을 실현한 클래스는 각각 MyAgent1입니다. MyAgent2 프로그램이 실행되는 순서는 다음과 같습니다.
MyAgent1.premain -> MyAgent2.premain -> MyProgram.main
(5) 또한 main 함수 뒤에 놓인 premain은 실행되지 않습니다. 예를 들어 다음과 같습니다.
java -javaagent:MyAgent1.jar -jar MyProgram.jar -javaagent:MyAgent2.jar
My Agent2는 My Program에 있습니다.jar 뒤에 있기 때문에 MyAgent2의premain이 실행되지 않기 때문에 실행된 결과는 다음과 같습니다.
MyAgent1.premain -> MyProgram.main
(6) 모든 자바 에이전트는 문자열 형식의 매개 변수, 즉premain의 에이전트 Args를 수신할 수 있다. 이 에이전트 Args는 자바 옵션을 통해 정의된 것이다.예:
java -javaagent:MyAgent2.jar=thisIsAgentArgs -jar MyProgram.jar
MyAgent2에서 premain이 수신한 에이전트 Args의 값은 "thisIsAgent Args"입니다.
(7) 매개 변수의 Instrumentation: 매개 변수의 Instrumentation inst를 통해 자신이 정의한 ClassFileTransformer를 추가하여class 파일을 변경합니다.여기에 사용자 정의된transformer는transform 방법을 실현했고 이 방법에서 실제 실행할 클래스의 바이트 코드에 대한 수정을 제공했으며 심지어 다른 클래스 방법을 실행할 수 있는 지경에 이르렀다.예: 쓰기 에이전트 클래스:

package org.toy;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassFileTransformer;
public class PerfMonAgent {
  private static Instrumentation inst = null;
  /**
   * This method is called before the application's main-method is called,
   * when this agent is specified to the Java VM.
   **/
  public static void premain(String agentArgs, Instrumentation _inst) {
    System.out.println("PerfMonAgent.premain() was called.");
    // Initialize the static variables we use to track information.
    inst = _inst;
    // Set up the class-file transformer.
    ClassFileTransformer trans = new PerfMonXformer();
    System.out.println("Adding a PerfMonXformer instance to the JVM.");
    inst.addTransformer(trans);
  }
}

ClassFileTransformer 클래스 쓰기:


package org.toy;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.NotFoundException;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
public class PerfMonXformer implements ClassFileTransformer {
  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    byte[] transformed = null;
    System.out.println("Transforming " + className);
    ClassPool pool = ClassPool.getDefault();
    CtClass cl = null;
    try {
      cl = pool.makeClass(new java.io.ByteArrayInputStream(
          classfileBuffer));
      if (cl.isInterface() == false) {
        CtBehavior[] methods = cl.getDeclaredBehaviors();
        for (int i = 0; i < methods.length; i++) {
          if (methods[i].isEmpty() == false) {
            doMethod(methods[i]);
          }
        }
        transformed = cl.toBytecode();
      }
    } catch (Exception e) {
      System.err.println("Could not instrument " + className
          + ", exception : " + e.getMessage());
    } finally {
      if (cl != null) {
        cl.detach();
      }
    }
    return transformed;
  }
  private void doMethod(CtBehavior method) throws NotFoundException,
      CannotCompileException {
    // method.insertBefore("long stime = System.nanoTime();");
    // method.insertAfter("System.out.println(\"leave "+method.getName()+" and time:\"+(System.nanoTime()-stime));");
    method.instrument(new ExprEditor() {
      public void edit(MethodCall m) throws CannotCompileException {
        m.replace("{ long stime = System.nanoTime(); $_ = $proceed($$); System.out.println(\""
                + m.getClassName()+"."+m.getMethodName()
                + ":\"+(System.nanoTime()-stime));}");
      }
    });
  }
}

위의 두 종류는 에이전트의 핵심입니다. jvm가 시작되면 응용 프로그램이 불러오기 전에 PerfMon Agent를 호출합니다.premain, 그리고 PerfMonAgent.premain에서 맞춤형 ClassFileTransforme인 PerfMonXformer를 실례화하고 inst.addTransformer(trans)를 통해PerfMonXformer의 실례를 Instrumentation 실례(jvm에서 전송)에 추가하면 응용 프로그램의 클래스가 불러올 때 PerfMonXformer가 불러옵니다.transform은 모두 호출됩니다. 이 방법에서 불러오는 클래스를 바꿀 수 있습니다. 정말 신기합니다. 클래스의 바이트 코드를 바꾸기 위해 저는 jboss의javassist를 사용했습니다. 꼭 이렇게 사용해야 하는 것은 아니지만 jboss의javassist는 정말 강력합니다. 클래스의 바이트 코드를 쉽게 바꿀 수 있습니다.
위의 방법에서 나는 클래스의 바이트 코드를 바꾸어 각 클래스의 방법 입구에long stime = System을 넣었다.nanoTime();,방법의 출구에 시스템을 넣었다.out.println(“methodClassName.methodName:”+(System.nanoTime()-stime));
읽어주셔서 감사합니다. 여러분에게 도움이 되었으면 좋겠습니다. 본 사이트에 대한 지지에 감사드립니다!

좋은 웹페이지 즐겨찾기