想必不少人听说过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 你假笨 阅读(2193) |
评论 (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 你假笨 阅读(1564) |
评论 (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 你假笨 阅读(3078) |
评论 (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从句引用到两个有着相同名称的表,则必须使用假名。如:
1select p.playerno
2from players as p,players as par
3where par.fn="jp" and par.ln="l" and p.birth_date<par.birth_date
对于多个表间的连接处理可能会导致有相同的结果,即有重复的结果,sql并不会自动从最终结果中删除重复的行,这是如果我们不希望在结果中出现重复的行,那么我们可以在select后直接指定distinct。如:
1select distinct T.playerno
2from teams as T,penalties as pen
3where T.playerno=pen.playerno。
接下来说说连接哈,对于内连接,如果是两个表的话,就取两个表的一个交集,如果是左外连接的话,那就是左边的表全取,右边没有的用null替代,弱国是右外连接的话,那就是右边的表全取,左边没有的用null表示。下面看看一个具体的例子:
1--表stu --表exam
2id name id grade
31, Jack 1, 56
42, Tom 2, 76
53, Kity 11, 89
64, nono
内连接 (显示两表id匹配的)
1select stu.id,exam.id,stu.name, exam.grade from stu (inner) join exam on stu.id=exam.id
2--------------------------------
31 1 Jack 56
42 2 Tom 76
左连接(显示join 左边的表的所有数据,exam只有两条记录,所以stu.id,grade 都用NULL 显示)
1select stu.id,exam.id,stu.name, exam.grade from stu left (outer) join exam on stu.id=exam.id
21 1 Jack 56
32 2 Tom 76
43 NULL Kity NULL
54 NULL nono NULL
右连接(与作连接相反,显示join右边表的所有数据)
1select stu.id,exam.id,stu.name, exam.grade from stu right join exam on stu.id=exam.id
21 1 Jack 56
32 2 Tom 76
4NULL 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的虚拟表:
1select *
2from (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
9order by town;
结果为:
1town number
2----------------------
3Douglas 2
4Inglewood 1
5Plymouth 6
6Stratford 4
posted @
2009-09-24 15:51 你假笨 阅读(2012) |
评论 (0) |
编辑 收藏