Java Agent

一、什么是 Java Agent ?

籠統地來講,Java Agent 是一個統稱,該功能是 Java 虛擬機提供的一整套后門。通過這套后門可以對虛擬機方方面面進行監控與分析。甚至干預虛擬機的運行。

Java Agent 又叫做 Java 探針,Java Agent 是在 JDK1.5 引入的,是一種可以動態修改 Java 字節碼的技術。Java 類編譯之后形成字節碼被 JVM 執行,在 JVM 在執行這些字節碼之前獲取這些字節碼信息,并且通過字節碼轉換器對這些字節碼進行修改,來完成一些額外的功能,這種就是 Java Agent 技術。

從用戶使用層面來看,Java Agent 一般通過在應用啟動參數中添加 -javaagent 參數添加 ClassFileTransformer 字節碼轉換器。 在Java虛擬機啟動時,執行main() 函數之前,Java 虛擬機會先找到 -java agent 命令指定 jar 包,然后執行 premain-class 中的 premain() 方法。用一句概括其功能的話就是:main() 函數之前的一個攔截器。

二、Java Agent 可以實現什么樣的功能?

從上面提到的字節碼轉換器的兩種執行方式來看可以實現如下功能:Java Agent 能夠在加載 Java 字節碼之前進行攔截并對字節碼進行修改;在 Jvm 運行期間修改已經加載的字節碼。

因此,通過以上兩點即可實現在一些框架或是技術的采集點進行字節碼修改,對應用進行監控(比如通過JVM CPU Profiler 從CPU、Memory、Thread、Classes、GC等多個方面對程序進行動態分析),或是對執行指定方法或接口時做一些額外操作,比如打印日志、打印方法執行時間、采集方法的入參和結果等;

基于前面對 Java Agent 大致機制的描述,我們不難猜到,能夠干預 Java JVM 虛擬機的運行,那么就可以解決不限于如下的問題:
使用 JVMTI 對 class 文件加密:有時一些涉及到關鍵技術的 class 文件或者 jar 包我們不希望對外暴露,因而需要進行加密。使用一些常規的手段(例如使用混淆器或者自定義類加載器)來對 class 文件進行加密很容易被反編譯。反編譯后的代碼雖然增加了閱讀的難度,但花費一些功夫也是可以讀懂的。使用 JVMTI 我們可以將解密的代碼封裝成 .dll, 或 .so 文件。這些文件想要反編譯就很麻煩了,另外還能加殼。解密代碼不能被破解,從而也就保護了我們想要加密的 class 文件。

使用 JVMTI 實現應用性能監控(APM) 在微服務大行其道的環境下,分布式系統的邏輯結構變得越來越復雜。這給系統性能分析和問題定位帶來了非常大的挑戰。基于JVMTI的APM能夠解決分布式架構和微服務帶來的監控和運維上的挑戰。APM通過匯聚業務系統各處理環節的實時數據,分析業務系統各事務處理的交易路徑和處理時間,實現對應用的全鏈路性能監測。開源的Skywalking、Pinpoint,、ZipKin、 Hawkular, 商業的 AppDynamics、OneAPM、Google Dapper等都是個中好手。

另外來看看 Github 上有哪些開源工具、項目使用到了 Agent 技術:
(1)阿里巴巴開源的Java診斷工具— Arthas,深受開發者喜愛。在線排查問題,無需重啟;動態跟蹤 Java 代碼;實時監控JVM狀態。
(2)Apache Skywalking 的 Java Agent 則針對服務的調用鏈路、JVM 基礎監控信息進行采集。
(3)Uber/jvm-profiler: 通過 Java Agent 采集 JVM CPU、Memory、IO等指標并發送給 Kafka、Console 以及可以自定義的發送器。

三、Java Agent 的實現原理?

從 JVM 類加載流程來看,字節碼轉換器的執行方式有兩種:一種是在 main 方法執行之前,通過 premain 來實現,另一種是在程序運行中,通過 Attach Api 來實現。

對于JVM內部的Attach實現,是通過 tools.jar 這個包中的 com.sun.tools.attach.VirtualMachine以及VirtualMachine.attach(pid) 這種方式來實現的。底層則是通過 JVMTI 在運行前或者運行時,將自定義的 Agent 加載并和 VM 進行通信。

了解 Java Agent 的實現原理就必須先了解 Java 的類加載機制(這里不做過多介紹),這個是了解 Java Agent 的前提。

JVM 在類加載時觸發 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK 事件調用添加的字節碼轉換器完成字節碼轉換,該過程時序如下:
在這里插入圖片描述

Java Agent 所使用的 Instrumentation 依賴 JVMTI 實現,當然也可以繞過 Instrumentation 直接使用 JVMTI 實現 Agent。因此,JVMTI 與 JDI 組成了 Java 平臺調試體系(JPDA)的主要能力。

如果想要深入了解 Java Agent,就得需要了解 JVMTI 以及 JVMTIAgent,下面分別介紹下:

JVMTI

JVMTI 是JVM Tool Interface 的縮寫,是 JVM 暴露出來給用戶擴展使用的接口集合,JVMTI 是基于事件驅動的,JVM每執行一定的邏輯就會調用一些事件的回調接口,這些接口可以給用戶自行擴展來實現自己的邏輯。JVMTI是實現 Debugger、Profiler、Monitor、Thread Analyser 等工具的統一基礎,在主流 Java 虛擬機中都有實現。

JVMTI Agent

JVMTI 并不一定在所有的 Java 虛擬機上都有實現,不同的虛擬機的實現也不盡相同。不過在一些主流的虛擬機中,比如 Sun 和 IBM,以及一些開源的如 Apache Harmony DRLVM 中,都提供了標準 JVMTI 實現。

JVMTI 是一套本地代碼接口,因此使用 JVMTI 需要我們與 C/C++ 以及 JNI 打交道。事實上,開發時一般采用建立一個 Agent 的方式來使用 JVMTI,它使用 JVMTI 函數,設置一些回調函數,并從 Java 虛擬機中得到當前的運行態信息,并作出自己的判斷,最后還可能操作虛擬機的運行態。把 Agent 編譯成一個動態鏈接庫之后,我們就可以在 Java 程序啟動的時候來加載它(啟動加載模式),也可以在 Java 5 之后使用運行時加載(活動加載模式)。
-agentlib:agent-lib-name=options
-agentpath:path-to-agent=options

JVMTIAgent主要有三個方法:
(1)Agent_OnLoad 方法,如果 agent 在啟動時加載,就執行這個方法
(2)Agent_OnAttach方法,如果agent不是在啟動的時候加載的,是我們先attach到目標線程上,然后對對應的目標進程發送load命令來加載agent,在加載過程中調用Agent_OnAttach函數
(3)Agent_OnUnload 方法,在 agent 做卸載掉時候調用

Instrument Agent

說到 javaagent,必須要講的是一個叫做 instrument 的 JVMTI Agent(Linux下對應的動態庫是 libinstrument.so) instrument agent 實現了上面 Agent_OnLoad 方法和 Agent_OnAttach 方法,也就是即能在啟動的時候加載 agent,也可以在運行期來加動態加載 agent,運行期動態加載 agent 依賴 JVM 的 attach 機制實現,通過發送 load 命令來加載 agent。那么什么是 JVM Attach 機制?

JVM Attach 機制

Jvm attach 機制是指 JVM 提供的一種 JVM 進程間通信的功能,能讓一個進程傳命令給另一個進程,并進行一些內部的操作,比如進行線程 dump,那么就需要執行 jstack 進行,然后把 pid 等參數傳遞給需要 dump 的線程來執行,這就是一種 java attach。

四、可以實現 Java Agent 的技術框架有哪些?

原理了解清楚了就需要實現,Java Agent 從實現上來看主要涉及到字節碼增強的過程,其到過程大概是:

  1. 修改字節碼
  2. 加載新的字節碼
  3. 替換舊的字節碼

通過上面對 Java Agent 介紹之后,是不是發現,我想要實現一個 Java Agent 還得去深入學習那么多東西嗎?當然不用,這里就介紹幾個常用的字節碼增強工具:

ASM: 對于需要手動操縱字節碼的需求,可以使用 ASM,它可以直接生成 .class 字節碼文件,也可以在類被加載入 JVM 之前動態修改類行為。
Javassist: ASM 是在指令層次上操作字節碼的,我們的直觀感受是在指令層次上操作字節碼的框架實現起來比較晦澀。故除此之外,再簡單介紹另外一類框架:強調源代碼層次操作字節碼的框架Javassist。
Javassist: 利用 Javassist 實現字節碼增強時,可以無須關注字節碼刻板的結構,其優點就在于編程簡單。直接使用 Java 編碼的形式,而不需要了解虛擬機指令,就能動態改變類的結構或者動態生成類。
Instrument: Instrument 是 JVM 提供的一個可以修改已加載類的類庫,專門為 Java 語言編寫的插樁服務提供支持。它需要依賴 JVMTI 的 Attach API 機制實現。
Byte Buddy: ByteBuddy 是一個開源 Java 庫,其主要功能是幫助用戶屏蔽字節碼操作,以及復雜的 InstrumentationAPI。ByteBuddy 提供了一套類型安全的API和注解,我們可以直接使用這些 API 和注解輕松實現復雜的字節碼操作。另外,Byte Buddy 提供了針對 Java Agent 的額外 API,幫助開發人員在 Java Agent 場景輕松增強已有代碼。

總結:

  1. java agent是一種動態修改java字節碼的技術,在jvm執行字節碼前通過字節碼轉換器對字節碼進行修改來實現額外的功能,相當于main函數執行前的一個攔截器。
  2. java agent能夠在加載java字節碼之前對字節碼進行修改,也可以在運行期間修改已加載的字節碼。可以對應用進行監控,對特定的接口或方法增加切面邏輯。
  3. java agent底層通過jvmti在運行前或運行時,加載自定義的agent并和vm通信。我們常用的字節碼增強工具有:ASM、Instrument等。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450881.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450881.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450881.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JDK 1.8 官網下載地址(linux / windows)

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 JDK 1.8 官網下載地址: JDK 1.8 官網下載地址(linuxwindows) 上面連接可以直接點擊,連接…

跟著太白老師學python day11 可迭代對象和迭代器

如果對象的屬性中有__iter__屬性就說明是可迭代的,容器類的數據類型都是可迭代對象 如果對象的屬性中既有__iter__屬性也有__next__屬性,就說明這個對象是迭代器 如何判斷一個函數是不是可迭代的,是不是迭代器 方法一 s 123 print(__iter__ …

攪局者奇虎360:特供機背后的周式隱憂

摘要:從未涉足制造,將來也“絕不參與制造”的360,卻聲稱要進軍智能手機。從未涉足制造,將來也“絕不參與制造”的360,卻聲稱要進軍智能手機。 5月4日晚間,“長達半年的思考”后,奇虎360董事長周…

數據結構基礎入門知識

數據結構基礎入門知識 ------ 數據結構:理解和練習《異類-不一樣的成功啟示錄》IP/26 192IP/25 128IP/24192.12864 3216 2 1 NTP:時間同步服務器高手都是最仔細的,嚴謹的 telnet 查看某個端口是否可用,是否是開啟狀態的要有探索冒險的精神 --…

Linux下安裝jdk8步驟詳述

見:https://www.cnblogs.com/shihaiming/p/5809553.html 0.下載jdk8 登錄網址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 選擇對應jdk版本下載。(可在Windows下下載完成后,通過文件夾…

用Kotlin擼一個圖片壓縮插件-實戰篇(三)

簡述: 由于個人原因,已經有很長一段時間沒有寫過文章,有句話是那么說的只要開始就不會太晚,所以我們開始《用Kotlin擼一個圖片壓縮插件》系列文章最后一篇實戰篇。實際上我已經把源碼發布到了GitHub,代碼很簡單。有了前兩篇文章的…

后端進階技術總結

1、SDK與API的區別? SDK是Software Development Kit的縮寫,即軟件開發工具包。可以把SDK想象成一個虛擬的程序包,在這個程序包中有一份做好的軟件功能,這份程序包幾乎是全封閉的,通過接口聯通外界,相應的接…

最簡容器化動手小實踐——再戰flappybird

《Flappy Bird》是一名越南開發者所開發的游戲,這款游戲的主要內容是幫助一只小鳥穿越水管的層層阻礙,玩家所需要的只是點擊屏幕從而調整小鳥的高度。而令這款游戲與眾不同的是,這款游戲的難度夸張的驚人,大多數玩家在初次上手之后…

為什么電影電視幀率不取整數?

英文名稱:time code時間碼概念時間碼(time code)是攝像機在記錄圖像信號的時候,針對每一幅圖像記錄的唯一的時間編碼。一種應用于流的數字信號。該信號為視頻中的每個幀都分配一個數字,用以表示小時、分鐘、秒鐘和幀數…

linux下用rpm 安裝jdk

1.下載jdk的rpm安裝包,這里以jdk-7u4-linux-i586.rpm為例進行說明 下載地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 2. 將jdk-7u4-linux-i586.rpm 移動到合適的安裝目錄上,安裝軟件不要在/home路徑下&#xf…

FFT 入門

推薦博客 : https://oi.men.ci/fft-notes/ 卷積的理解 : https://www.zhihu.com/question/22298352?rf21686447 題目鏈接 :http://uoj.ac/problem/34 這是一道模板題。給你兩個多項式,請輸出乘起來后的多項式。輸入格式第一行兩個…

MPEG4視頻壓縮編碼技術詳解

MPEG全稱是Moving Pictures Experts Group,它是“動態圖象專家組”的英文縮寫,該專家組成立于1988年,致力于運動圖像及其伴音的壓縮編碼標準化工作,原先他們打算開發MPEG1、MPEG2、MPEG3和MPEG4四個版本,以適用于不同帶…

oracle orion hugepages_settings.sh(支持OEL 7,4.1內核)

orion需要首先配置hugepage,否則會出現下列錯誤。[rootyyxxdb01 ~]# /opt/app/11.2.0/grid_home/bin/orion -run oltp -testname mytestORION: ORacle IO Numbers -- Version 11.2.0.4.0************************ Large Pages Information *******************Param…

eclipse啟動出現“An Error has Occurred. See the log file”解決方法

見:http://blog.csdn.net/ww130929/article/details/52652222 這段時間開發java的項目,剛開始啟動Eclipse的時候經常遇到這個問題,寫這篇博客來記錄解決方法。 1.刪除工程目錄下的: “.metadata/.plugins/org.eclipse.core.resour…

初識NIO之Java小Demo

Java中的IO、NIO、AIO: BIO:在Java1.4之前,我們建立網絡連接均使用BIO,屬于同步阻塞IO。默認情況下,當有一條請求接入就有一條線程專門接待。所以,在客戶端向服務端請求時,會詢問是否有空閑線程…

RTP協議詳解

RTP協議分析 第1章. RTP概述 1.1. RTP是什么 RTP全名是Real-time Transport Protocol(實時傳輸協議)。它是IETF提出的一個標準,對應的RFC文檔為RFC3550(RFC1889為其過期版本)。RFC3550不僅定義了RTP&#xff0…

線程狀態轉換

一、線程狀態轉換 新建(New) 創建后尚未啟動。 可運行(Runnable) 可能正在運行,也可能正在等待 CPU 時間片。 包含了操作系統線程狀態中的 Running 和 Ready。 阻塞(Blocking) 等待獲取一個排它…

Eclipse中啟動tomcat報錯java.lang.OutOfMemoryError: PermGen space的解決方法

見:http://outofmemory.cn/java/OutOfMemoryError/outofmemoryerror-permgen-space-in-tomcat-with-eclipse 有的項目引用了太多的jar包,或者反射生成了太多的類,異或有太多的常量池,就有可能會報java.lang.OutOfMemoryError: Per…

MPEG-4 AVC/H.264 信息

作者:haibara 來源:pcicp.com 本FAQ由(haibara)翻譯,期間受到kaito_mkid(pcicp)幫助,在此感謝,由于Newbie的關系,如有翻譯錯誤,還請各位指出&…

eclipse搜索關鍵字

見:https://jingyan.baidu.com/article/e6c8503c1a60d2e54f1a18e3.html