想必不少人聽說過javaagent,但是很少人聽說Instrumentation,其實Instrumentation就是javaagent的實現機制,說到Instrumentation,就必須想了解java的attach機制,那就先說下attach的實現。

大家進行jstack的時候,是不是經常看到兩個線程Signal Dispatcher和 Attach Listener線程,可能不知道是干嘛的吧,這兩個線程是實現attach的關鍵所在,其中前者是在jvm啟動的時候就會創建的,后者只有接收過attach請求的時候vm才會創建,顧名思義,Signal Dispatcher是分發信號的, Attach Listener 是處理attach請求的,那么兩者有什么關系呢,當我們執行attach方法的時候,會向目標vm發出一個SIGQUIT 的信號,目標vm收到這個信號之后就會創建Attach Listener線程了,當然jvm保證了不會多創建。
1 path = findSocketFile(pid);
2 if (path == null) {
3 File f = new File(tmpdir, ".attach_pid" + pid);
4 createAttachFile(f.getPath());
5 try {
6 sendQuitTo(pid);
7
8 // give the target VM time to start the attach mechanism
9 int i = 0;
10 long delay = 200;
11 int retries = (int)(attachTimeout() / delay);
12 do {
13 try {
14 Thread.sleep(delay);
15 } catch (InterruptedException x) { }
16 path = findSocketFile(pid);
17 i++;
18 } while (i <= retries && path == null);
19 if (path == null) {
20 throw new AttachNotSupportedException(
21 "Unable to open socket file: target process not responding " +
22 "or HotSpot VM not loaded");
23 }
24 } finally {
25 f.delete();
26 }
27 }
Attach機制說得簡單點就是提供A進程可以連上B進程(當然是java進程),創建socket進行通信,A通過發命令給B,B然后對命令進行截取從自己的vm中獲取信息發回給客戶端vm,但是并不是隨便發指令都會處理的,那么attach Listener接收哪些命令呢,如下所示
static AttachOperationFunctionInfo funcs[] = {
{ "agentProperties", get_agent_properties },
{ "datadump", data_dump },
{ "dumpheap", dump_heap },
{ "load", JvmtiExport::load_agent_library },
{ "properties", get_system_properties },
{ "threaddump", thread_dump },
{ "inspectheap", heap_inspection },
{ "setflag", set_flag },
{ "printflag", print_flag },
{ "jcmd", jcmd },
{ NULL, NULL }
};
Instrumentation的實現其實主要使用了load這個指令,它用來實現讓target vm動態加載agentlib,Instrumentation的實現在一個名為libinstrument.dylib的動態lib庫,linux下是libinstrument.so,它是基于jvmti接口實現的,因此在對其進行load的時候會創建一個agent實例,并往jvmti環境注冊一些回調方法,比如監聽類文件加載的事件,vm初始化完成事件等,執行Agent_OnAttach,這里會創建一個Instrumentation實例并返回給用戶供大家擴展Instrumentation,比如增加一些transform。并會執行Instrumentation實例的loadClassAndCallAgentmain方法,該方法主要執行agent的MF文件里定義的 Agent-Class類的agentmain方法,當vm初始化完畢之后,會調用loadClassAndCallPremain方法,該方法主要執行agent的MF文件里定義的 Agent-Class類的pre main方法。在類進行加載的時候會調用Instrumentation的transform方法,可以看看參數里有個byte數組,這個數組其實就是正在加載的class字節碼,所以如果要字節碼增強在這里就可以入手啦,甚至可以實現偷天換日.
posted @
2013-04-12 22:38 你假笨 閱讀(2204) |
評論 (0) |
編輯 收藏
最近在忙一個項目,使用的是Flex+Spring+Hibernate,期間碰到一個問題,有必要在此記錄一下,也方便有相似問題的來者參考下
問題描述:有一個用戶表和一個用戶詳情表,這兩個表是一個一對一的單向關聯關系,即在用戶表中一個外鍵引用用戶詳情表,我在UserInfo的映射文件中使用的是many-to-one,設置了unique="true"表示一對一關系,設置了cascade="save-update"表示的是在保存useInfo對象的時候會自動保存與之關聯的userDetails臨時對象,即我希望的是先執行一個在用戶詳情表中的插入語句然后再執行一個在用戶表中的插入語句,userInfo對象是從flex端傳過來的,當然也設置了userDetails屬性的值,在userInfo的dao文件中save方法是這樣的
public IvUserInfo save(IvUserInfo transientInstance) {
log.debug("saving IvUserInfo instance");
try {
getHibernateTemplate().save(transientInstance);
log.debug("save successful");
} catch (RuntimeException re) {
log.error("save failed", re);
throw re;
}
return transientInstance;
}
后面發現執行的sql語句只有一條插入語句,就是在用戶表中的一個插入,由于外鍵的關聯作用,是用戶表的這條插入也無法執行,這就是問題所在了。
問題解決:這個問題我也沒有具體研究Hibernate的源碼,我先寫了個測試類,發現僅僅在java中執行操作的話是可以正確執行兩條插入語句的,但是通過flex傳過來就有問題了,那說明是flex端傳參數過來的問題,于是我試著修改UserInfo的save方法:
public IvUserInfo save(IvUserInfo transientInstance) {
log.debug("saving IvUserInfo instance");
try {
IvUserDetails ud=new IvUserDetails();
ud.setQq(transientInstance.getIvUserDetails().getQq());
transientInstance.setIvUserDetails(ud);
getHibernateTemplate().save(transientInstance);
log.debug("save successful");
} catch (RuntimeException re) {
log.error("save failed", re);
throw re;
}
return transientInstance;
}
這樣一來問題解決了,順利執行了兩條插入語句。
如果朋友知道具體原因的話希望給我留言了,同時也希望該記錄能幫助碰到此類問題的朋友。
posted @
2010-06-22 11:20 你假笨 閱讀(1572) |
評論 (0) |
編輯 收藏
歡迎光臨筆者博客
http://www.lovestblog.cn
最近兩天本人在為本博實現rss發布和訂閱,本來是想在前端實現xml的生成和修改,因為用as3的E4X操作xml比較方便,但是后面發現不能為元素設置CDATA值,于是只好作罷,便只能依靠后臺的java來實現此功能了,當然操作xml的話,我首先想到了dom4j,dom4j操作xml還是比較方便的,即可以輕松實現我們的CDATA設置,也可以為我們任意位置插入元素提供了實現,對于在指定位置新增節點開始我有點蒙了,后面通過網上搜索資源加之自己的一些理解,而實現了此功能,下面展示了部分代碼供今后參考吧:
-

public static int createXMLFile(String filename,List list)
{

/** *//** 返回操作結果, 0表失敗, 1表成功 */
int returnValue = 0;
Document document = DocumentHelper.createDocument();
Element rssElement = document.addElement("rss");
rssElement.addAttribute("version", "2.0");
Element channelElement = rssElement.addElement("channel");
Element titleElement = channelElement.addElement("title");
titleElement.setText("你假笨(nijiaben)心情技術博客");
Element linkElement = channelElement.addElement("link");
linkElement.setText("http://www.lovestblog.cn");
Element descriptionElement = channelElement.addElement("description");
descriptionElement.setText("專注于Java,Flex技術開發研究");
Element languageElement = channelElement.addElement("language");
languageElement.setText("zh-cn");
Element lastBuildDateElement = channelElement.addElement("lastBuildDate");
lastBuildDateElement.setText(new java.text.SimpleDateFormat("yyyy-mm-dd hh:mm:ss",Locale.CHINA).format(((ArticleInfo)(list.get(0))).getCreateTime()));

for(int i=list.size()-1;i>0;i--)
{
ArticleInfo ainfo=(ArticleInfo)(list.get(i));
Element itemElement = channelElement.addElement("item");
Element title1Element = itemElement.addElement("title");
title1Element.setText(ainfo.getTitle());
Element description1Element = itemElement.addElement("description");
int maxLen=5000;

if(ainfo.getRssContent().length()<5000)
{
maxLen=ainfo.getRssContent().length();
}
description1Element.addCDATA(ainfo.getRssContent().substring(0, maxLen));
Element pubDate=itemElement.addElement("pubDate");
pubDate.setText(new java.text.SimpleDateFormat("yyyy-mm-dd hh:mm:ss",Locale.CHINA).format(ainfo.getCreateTime()));
Element link1Element=itemElement.addElement("link");
link1Element.setText("http://www.lovestblog.cn");
}

try
{

/** *//** 將document中的內容寫入文件中 */
XMLWriter writer = new XMLWriter(new FileOutputStream(path+filename));
writer.write(document);
writer.close();

/** *//** 執行成功,需返回1 */
returnValue = 1;

}catch(Exception ex)
{
ex.printStackTrace();
}
return returnValue;
}

posted @
2010-04-01 12:14 你假笨 閱讀(3088) |
評論 (1) |
編輯 收藏
本文最新發布于http://www.lovestblog.cn,歡迎轉載該文,但請注明文章出處,謝謝合作。
mysql的from從句用來指定參與查詢的表,當然也可以是生成的中間表,在表前我們有時需要指定數據庫,這主要是用在我們需要訪問當前數據庫之外的數據庫中的表的情況,在這中情況下我們采用"."操作符來進行,如userdb.user,其實userdb為數據庫名,user為表名,這是對mysql數據庫而言的,對于DB2和Oracle就不是通過指定數據庫名了,而是指定sql用戶了,這就是說不同sql用戶可以建立相同名字的表,但是同一個sql用戶只能建立唯一名字的表。這就是它們在這表規范上面的區別。對于列規范,mysql可以在需要查詢的列則可以采用如下形式進行訪問:“數據庫名.表名.列名”。對于多個表的規范,也就是涉及查詢多個表的情況下,執行的過程是采用笛卡爾積的形式進行的。也就是說生成的中間表的列數為兩個表中列數的總和,而行的總數等于一個表中的行的數量與另外一個表中行的數量的乘積。
對于from從句中使用假名的情況,比如select u.id,name,age,a.account from utb as u,atb as a where u.id=a.user_id,在我們使用假名之后,那么在該sql語句的任何地方都只能使用假名,不能使用真實的表名,同時上面的as關鍵字也是可以省略的,也就是說對于上面的語句不能用atb來取代a,utb來取代u了。雖然from從句不是我們指定的第一條語句,但是絕對是第一個被處理的語句,所以在聲明假名前使用假名不會導致錯誤。如果一條from從句引用到兩個有著相同名稱的表,則必須使用假名。如:
1
select p.playerno
2
from players as p,players as par
3
where par.fn="jp" and par.ln="l" and p.birth_date<par.birth_date
對于多個表間的連接處理可能會導致有相同的結果,即有重復的結果,sql并不會自動從最終結果中刪除重復的行,這是如果我們不希望在結果中出現重復的行,那么我們可以在select后直接指定distinct。如:
1
select distinct T.playerno
2
from teams as T,penalties as pen
3
where T.playerno=pen.playerno。
接下來說說連接哈,對于內連接,如果是兩個表的話,就取兩個表的一個交集,如果是左外連接的話,那就是左邊的表全取,右邊沒有的用null替代,弱國是右外連接的話,那就是右邊的表全取,左邊沒有的用null表示。下面看看一個具體的例子:
1
--表stu --表exam
2
id name id grade
3
1, Jack 1, 56
4
2, Tom 2, 76
5
3, Kity 11, 89
6
4, nono
內連接 (顯示兩表id匹配的)
1
select stu.id,exam.id,stu.name, exam.grade from stu (inner) join exam on stu.id=exam.id
2
--------------------------------
3
1 1 Jack 56
4
2 2 Tom 76
左連接(顯示join 左邊的表的所有數據,exam只有兩條記錄,所以stu.id,grade 都用NULL 顯示)
1
select stu.id,exam.id,stu.name, exam.grade from stu left (outer) join exam on stu.id=exam.id
2
1 1 Jack 56
3
2 2 Tom 76
4
3 NULL Kity NULL
5
4 NULL nono NULL
右連接(與作連接相反,顯示join右邊表的所有數據)
1
select stu.id,exam.id,stu.name, exam.grade from stu right join exam on stu.id=exam.id
2
1 1 Jack 56
3
2 2 Tom 76
4
NULL 11 NULL 89
內連接取交集,外連接分左和右,
左連接左邊的全取,
右連接右邊的全取
對于連接的列的名稱相同的話,那么可以使用using來替代條件,如上面的內連接可以這樣改寫:
1
select stu.id,exam.id,stu.name, exam.grade from stu inner join exam using(id)。
對于左外連接使用的情況一般是當左表的連接列中存在未出現在右表的連接列中的值時,左外連接才有用。
還有個全外連接的,也就是說只要在兩個表中出現的記錄都會在中間表中出現,當右表有而左表沒有或當左表有而右表沒有的時候用null表示。具體語法如下:select stu.id,exam.id,stu.name, exam.grade from stu full join exam using(id)。
交叉連接:就是顯示求表的笛卡爾積,select * from teams cross join penalties.這句完全等價于select teams.*,penalties.* from teams,penalties.
聯合連接:select * from teams union join penalties,這個其實很容易理解,產生結果所包含的列為兩個表所有的列和,對于數據的列出,首先列出左表的數據,對于屬于右表的列,用null表示,接下來列出右表的數據,對于屬于左表的列用null表示。
自然連接:select * from teams nature inner join penalties where division='first';此句完全等同與select t.playerno,t.teamno,t.division,pen.paymentno,pen.payment_date,pen.amount from teams as t inner join penalties as pen on t.playerno=pen.playerno where dividion='first'.相比就知道,我們無須顯示指出必須要連接到哪些列,sql會自動查找兩表中是否有相同名稱的列,且假設他們必須在連接條件中使用。此處的on或using從句是多余的,因此不允許使用。
下面看個例子創建一個稱為towns的虛擬表:
1
select *
2
from (select 'Stratford' as town,4 as number
3
union
4
select 'Plymouth',6
5
union
6
select 'Inglewood',1
7
union
8
select 'Douglas',2) as towns
9
order by town;
結果為:
1
town number
2
----------------------
3
Douglas 2
4
Inglewood 1
5
Plymouth 6
6
Stratford 4
posted @
2009-09-24 15:51 你假笨 閱讀(2027) |
評論 (0) |
編輯 收藏