Java性能監控之javassist探索


由於個人工作原因,近期遇到多起因應用性能導致業務中斷的事情。多次排查分析總結,發現是應用性能問題,當然性能的提現是多維度的,在這里就不贅述了。

主要關注在應用運行中斷之前就發現它(事前處理),是很重要的。

要監控應用的性能,首先列出性能監控點,然后輸出要關注的信息,最終根據信息進行數據分析得出性能瓶頸后進行持續優化改進,在問題爆發前將其扼殺在“子宮”里。

不同應用、不同場景下,監控點不盡相同,要關注的信息如何獲取卻是每個工程師都要思考的問題。

在接觸javassist之前,有過幾個方案,但發布了幾版后發現實現方式太low、成本高、效率低等不足,其中包括:代碼中嵌入日志、使用spring管理應用並使用aop、修改jar包源代碼增加日志。

如果是一個新搭建的工程,以上方案可以在框架搭建過程中便包含進去,作為框架的基礎能力隨同應用一起發布。但是平台上應用很多,明顯不是很適用,並且有些操作所帶來的風險需要更多的工作量去規避。

於是,找到了她------javassist。

 

javassist的使用要借助於javaagent技術,接下來介紹如何使用javassist

1、獲取javassit-3.20.0-GA.jar

2、創建類AgentTransformer並實現ClassFileTransformer接口,使用javassist API完成對源類字節碼級別的修改

3、創建類AgentDemo,並增加premain實現public static void premain(String args, Instrumentation inst){ inst.addTransformer(new AgentTransformer()); }

4、創建MAINFEST.MF文件,內容如下:

Manifest-Version: 1.0
Premain-Class: AgentDemo
Can-Redefine-Classes: true
Can-Retransform-Classes: true

5、打jar包:main class指定使用MAINFEST.MF文件

 

 

6、實際應用--創建demo應用,引入兩個jar包:javassist-3.20.0-GA.jar和javassistdemo.jar(上面打包出來的)

7、修改java啟動參數

8、運行應用程序,觀察結果

System.out.println("This code is inserted before constructor sun/misc/URLClassPath$FileLoader$1");
System.out.println("This code is inserted after constructor sun/misc/URLClassPath$FileLoader$1");
This code is inserted before constructor sun/misc/URLClassPath$FileLoader$1
This code is inserted after constructor sun/misc/URLClassPath$FileLoader$1
System.out.println("This code is inserted before constructor com/hope/javassistapp/app/App");
System.out.println("This code is inserted after constructor com/hope/javassistapp/app/App");
System.out.println("This code is inserted before constructor sun/misc/Cleaner");
System.out.println("This code is inserted after constructor sun/misc/Cleaner");
System.out.println("This code is inserted before constructor java/lang/Enum");
System.out.println("This code is inserted after constructor java/lang/Enum");
This code is inserted before constructor sun/misc/URLClassPath$FileLoader$1
This code is inserted after constructor sun/misc/URLClassPath$FileLoader$1
System.out.println("This code is inserted before constructor com/hope/javassistapp/construct/JavassistDemo1");
System.out.println("This code is inserted after constructor com/hope/javassistapp/construct/JavassistDemo1");
This code is inserted before constructor com/hope/javassistapp/construct/JavassistDemo1
JavassistDemo1:自身構造函數輸出內容
This code is inserted after constructor com/hope/javassistapp/construct/JavassistDemo1
This code is inserted before constructor sun/misc/URLClassPath$FileLoader$1
This code is inserted after constructor sun/misc/URLClassPath$FileLoader$1
System.out.println("This code is inserted before constructor com/hope/javassistapp/construct/JavassistDemo2");
System.out.println("This code is inserted after constructor com/hope/javassistapp/construct/JavassistDemo2");
This code is inserted before constructor com/hope/javassistapp/construct/JavassistDemo2
JavassistDemo2:自身構造函數輸出內容
This code is inserted after constructor com/hope/javassistapp/construct/JavassistDemo2
System.out.println("This code is inserted before constructor java/lang/Shutdown");
System.out.println("This code is inserted after constructor java/lang/Shutdown");
System.out.println("This code is inserted before constructor java/lang/Shutdown$Lock");
System.out.println("This code is inserted after constructor java/lang/Shutdown$Lock");
This code is inserted before constructor java/lang/Shutdown$Lock
This code is inserted before constructor java/lang/Shutdown$Lock
This code is inserted after constructor java/lang/Shutdown$Lock
This code is inserted after constructor java/lang/Shutdown$Lock
This code is inserted before constructor java/lang/Shutdown$Lock
This code is inserted before constructor java/lang/Shutdown$Lock
This code is inserted after constructor java/lang/Shutdown$Lock
This code is inserted after constructor java/lang/Shutdown$Lock

 

 

輸出結果中,黃色高亮部分是動態增加的代碼造成的效果。

AgentDemo和AgentTransformer是agent工程下的,用於動態修改類使用。

package com.hope.agent;

import java.lang.instrument.Instrumentation;

import com.hope.transform.AgentTransformer;

/**
 * java agent 入口
 * @author hp
 *
 */
public class AgentDemo {

    public static void premain(String args, Instrumentation inst) {
        inst.addTransformer(new AgentTransformer());
    }
}
AgentDemo
package com.hope.transform;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.LoaderClassPath;

/**
 * 對類字節碼轉譯
 * @author hp
 *
 */
public class AgentTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className,
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
            byte[] classfileBuffer) throws IllegalClassFormatException {
        /**
         * 此處使用javassist API對classfileBuffer進行修改
         */
        ClassPool pool = new ClassPool(true);
        pool.appendClassPath(new LoaderClassPath(loader));
        try {
            CtClass cls = pool.makeClass(new ByteArrayInputStream(classfileBuffer));
            
            //獲取構造函數數組
            CtConstructor[] ccs = cls.getDeclaredConstructors();
            //構造函數方法體開始時添加的代碼
            String codeStrBefore = "System.out.println(\"This code is inserted before constructor "+className+"\");";
            System.out.println(codeStrBefore);
            //構造函數方法體結束前添加的代碼
            String codeStrAfter = "System.out.println(\"This code is inserted after constructor "+className+"\");";
            System.out.println(codeStrAfter);
            for (CtConstructor cc : ccs) {
                cc.insertBefore(codeStrBefore);
                cc.insertAfter(codeStrAfter, true);
            }
            return cls.toBytecode();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            e.printStackTrace();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }
        
        return null;
    }

}
AgentTransformer

 

App和JavassistDemo1、JavassistDemo2用於demo演示使用

package com.hope.javassistapp.app;

import com.hope.javassistapp.construct.JavassistDemo1;
import com.hope.javassistapp.construct.JavassistDemo2;

public class App {

    /**
     * @param args
     */
    public static void main(String[] args) {
        JavassistDemo1 d1 = new JavassistDemo1();
        JavassistDemo2 d2 = new JavassistDemo2();
    }

}
App
package com.hope.javassistapp.construct;

public class JavassistDemo1 {

    public JavassistDemo1() {
        
        
        System.out.println("JavassistDemo1:自身構造函數輸出內容");
        
        
    }
}
JavassistDemo1
package com.hope.javassistapp.construct;

public class JavassistDemo2 {

    public JavassistDemo2() {
        
        
        System.out.println("JavassistDemo2:自身構造函數輸出內容");
        
        
    }
}
JavassistDemo2

 

對於javassist目前探索到這里,日后還會繼續加深對其理解和使用,敬請期待。

歡迎業內人士交流經驗。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM