- 浏览: 849835 次
- 性别:
- 来自: 南京
-
文章分类
最新评论
-
loveseed1989:
您好,我用您的方法运行Cone.java,会给我报java.l ...
vtk学习笔记 --- 编译vtk库和java库 -
60love5:
60love5 写道首先谢谢你的解析,但你这个验证可见性的小程 ...
多线程中共享对象的可见性 -
60love5:
首先谢谢你的解析,但你这个验证可见性的小程序是存在问题的,你的 ...
多线程中共享对象的可见性 -
Gamehu520:
...
java 中的Unsafe -
shanpao1234560:
这个list不是静态的第一种情况下也会有线程安全的问题么,求指 ...
一个看似线程安全的示例
3.2.5有状态转换
在前面章节看到的转换都是局部的,它们都没有依赖于之前已经访问过的代码:在方法最开始添加的代码始终是一样的并且总是会被添加,同样,在RETURN之前添加的代码也是如此。这样的转换称之为无状态转换。这些转换较容易实现,但是也只有这种最简单的转换才满足这个属性。
更复杂的转换,需要记住之前访问过代码的一些状态。考虑这样的一个转换,移除所有ICONST_0 IADD指令序列,这些指令的效果类似于加零。当一个IADD指令被访问时,只有当最后一个被访问指令是ICONST_0时,这个指令序列才可以被移除。这需要在方法适配器内部保存一些状态。类似于这样的转换称之为有状态转换。
让我们详细地了解下这个示例。当一个ICONST_0指令被访问时,如果其下一条指令是IADD时,才可以将这两条指令删除。问题是在访问ICONST_0指令时,下一条指令还未知。解决方案就是推迟最初这个判断知道下一条指令被访问:如果访问到IADD指令,那么就移除这两个指令,否则忽略ICONST_0指令和当前的指令。
为了实现这样的一个转换:移除或者替换某些指令序列,简便的方式就是引入一个方法适配器MethodAdapter,这个类的visitXxxInsn方法会调用一个共同的方法visitInsn方法:
public abstract class PatternMethodAdapter extends MethodAdapter {
protected final static int SEEN_NOTHING = 0;
protected int state;
public PatternMethodAdapter(MethodVisitor mv) {
super(mv);
}
@Overrid public void visitInsn(int opcode) {
visitInsn();
mv.visitInsn(opcode);
}
@Override public void visitIntInsn(int opcode, int operand) {
visitInsn();
mv.visitIntInsn(opcode, operand);
}
...
protected abstract void visitInsn();
}
上面的转换可以像下面这样实现:
public class RemoveAddZeroAdapter extends PatternMethodAdapter {
private static int SEEN_ICONST_0 = 1;
public RemoveAddZeroAdapter(MethodVisitor mv) {
super(mv);
}
@Override public void visitInsn(int opcode) {
if (state == SEEN_ICONST_0) {
if (opcode == IADD) {
state = SEEN_NOTHING;
return;
}
}
visitInsn();
if (opcode == ICONST_0) {
state = SEEN_ICONST_0;
return;
}
mv.visitInsn(opcode);
}
@Override protected void visitInsn() {
if (state == SEEN_ICONST_0) {
mv.visitInsn(ICONST_0);
}
state = SEEN_NOTHING;
}
}
visitInsn方法首先会判断指令序列是否被检测到。在这个例子中,它初始化state然后立即返回,它具有删除指令序列的效果。在另外的情形中,它会调用visitInsn方法,这个方法会
忽略ICONST_0指令。如果当前的指令是ICONST_0,它会记录下来,然后返回,这样就可以在下一条指令到来时判断了。在所有其它的情形中,当前的指令直接被传递给了下一个visitor。
标签和帧
如我们在前面章节中见到的一样,标签和帧会先于它们关联的指令被访问。换句话说,它们会和指令同时被访问,尽管它们本身不是指令。这会对检测指令序列这样的转换造成一定的影响,但这种影响事实上是有利的。如果我们移除的指令是跳转指令的目标,那会发生什么了?如果有指令会跳转到ICONST_0指令,这意味着这里有一个标签来代表这个指令。在移除这两个指令后,这个标签就会代表IADD之后的指令,这正是我们想要的。但是如果一些指令要跳转到IADD指令,那我们就不能移除这个指令序列了(我们不能确保在跳转之前,一个0值入栈了)。让人有希望的是,在这种情况下,在ICONST_0和IADD之间必须有一个标签,并且这很容易检测到。
这里的原因也同样适用于栈映射帧:如果一个栈映射帧在这两个指令之间被访问,我们也不能移除这两个指令。这两种情形,我们都可以把标签和帧当做指令使用模式匹配算法来处理。这可以使用PatternMethodAdapter类完成(注意visitMaxs方法也会调用visitInsn方法,这是用来处理这样的情形:方法的结尾可能是这个序列的前缀,它们也应该被检测到):
public abstract class PatternMethodAdapter extends MethodAdapter {
...
@Override public void visitFrame(int type, int nLocal, Object[] local,
int nStack, Object[] stack) {
visitInsn();
mv.visitFrame(type, nLocal, local, nStack, stack);
}
@Override public void visitLabel(Label label) {
visitInsn();
mv.visitLabel(label);
}
@Override public void visitMaxs(int maxStack, int maxLocals) {
visitInsn();
mv.visitMaxs(maxStack, maxLocals);
}
}
我们在下一章节中会看到,一个编译后的类可能会包含源文件行号等信息,可以用来进行异常追踪。这些信息通过visitLineNumber方法来访问,它们也会在指令被访问的同时被调用。尽管如此,出现在指令序列中间的行号也不会对转换或者删除行号有任何影响。相应的解决办法就是在模式匹配算法中完全忽略它们。
一个更复杂的例子
之前的例子可以很容易地扩展到更复杂的指令序列的情形。考虑这样转换示例,删除所有字段的自我赋值,如f=f,或者,以字节码的形式就是,ALOAD 0 ALOAD 0 GETFIELD f PUTFIELD f。在实现这个转换之前,我们最好来设计一个识别这种序列的状态机:
每个转换都会以一个条件作为标签(就是当前的指令)以及一个动作(一个必须被执行的指令序列,以粗体表示)。例如从S1到S0的转换,只有当前的指令不是ALOAD 0时才进行。在这种情形下,ALOAD 0会被访问然到达初始状态。来看看S2到自身的转换:如果找到3个或者更多的连续的ALOAD 0指令,那么将进行S2到自身的转换。在这种情形下,我们会停留在这样的状态:两条ALOAD 0指令已经被访问,同时我们将再执行一个ALOAD 0指令。一旦这个状态机模型被找到,那么编写对应的方法适配器就很直接了(下面的8个switch case就对应着上面的8个转换):
class RemoveGetFieldPutFieldAdapter extends PatternMethodAdapter {
private final static int SEEN_ALOAD_0 = 1;
private final static int SEEN_ALOAD_0ALOAD_0 = 2;
private final static int SEEN_ALOAD_0ALOAD_0GETFIELD = 3;
private String fieldOwner;
private String fieldName;
private String fieldDesc;
public RemoveGetFieldPutFieldAdapter(MethodVisitor mv) {
super(mv);
}
@Override
public void visitVarInsn(int opcode, int var) {
switch (state) {
case SEEN_NOTHING: // S0 -> S1
if (opcode == ALOAD && var == 0) {
state = SEEN_ALOAD_0;
return;
}
break;
case SEEN_ALOAD_0: // S1 -> S2
if (opcode == ALOAD && var == 0) {
state = SEEN_ALOAD_0ALOAD_0;
return;
}
break;
case SEEN_ALOAD_0ALOAD_0: // S2 -> S2
if (opcode == ALOAD && var == 0) {
mv.visitVarInsn(ALOAD, 0);
return;
}
break;
}
visitInsn();
mv.visitVarInsn(opcode, var);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name,
String desc) {
switch (state) {
case SEEN_ALOAD_0ALOAD_0: // S2 -> S3
if (opcode == GETFIELD) {
state = SEEN_ALOAD_0ALOAD_0GETFIELD;
fieldOwner = owner;
fieldName = name;
fieldDesc = desc;
return;
}
break;
case SEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -> S0
if (opcode == PUTFIELD && name.equals(fieldName)) {
state = SEEN_NOTHING;
return;
}
break;
}
visitInsn();
mv.visitFieldInsn(opcode, owner, name, desc);
}
@Override protected void visitInsn() {
switch (state) {
case SEEN_ALOAD_0: // S1 -> S0
mv.visitVarInsn(ALOAD, 0);
break;
case SEEN_ALOAD_0ALOAD_0: // S2 -> S0
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
break;
case SEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -> S0
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, fieldOwner, fieldName, fieldDesc);
break;
}
state = SEEN_NOTHING;
}
}
注意,因为和3.2.4节中AddTimerAdapter相似的原因,本节中出现的有状态转换不需要转换栈映射帧:原始的帧仍在转换之后然有效。也不需要转换局部变量和操作数栈的大小。最后,注意有状态转换不局限于检测和转换指令序列,还有很多其它类型的转换也是状态转换。下一节出现的有关方法适配器就是这样的例子。
发表评论
-
基于Oracle Streams + Oracle AQ 捕获变更,发布变更(二)
2014-11-21 22:23 3223要求:使用Oracle Streams捕获某个用户下部 ... -
基于Oracle Streams + Oracle AQ 捕获变更,发布变更(一)
2014-11-20 22:23 2824要求:使用Oracle Streams捕获某个用户下部分表 ... -
如何去掉在浏览器中打开java applet时的警告对话框
2013-08-24 12:10 7012好久没更新博客了! 最近,由于项目要求,需要将sw ... -
Android 内存泄露笔记
2013-03-05 23:10 01、大部分内存泄露都是错误的持有了Activity或者Con ... -
java 虚拟机总结 【思维导图】
2012-12-22 20:11 2107java虚拟机总结思维导图: 参考《深入理解jav ... -
Java虚拟机字节码执行引擎 【思维导图】
2012-12-22 19:51 1703java虚拟机字节码执行引擎思维导图总结: 参考《深入理 ... -
java 垃圾回收相关总结 【思维导图】
2012-12-21 19:03 3304java垃圾回收相关总结: 参考《深入理解java ... -
java并发中的延迟初始化
2012-12-12 19:17 4716在《java并发编程实战 ... -
java同步容器与并发容器
2012-12-09 18:07 4369何为同步容器:可以简 ... -
一个看似线程安全的示例
2012-12-09 14:56 5094在《java并发编程实战》第四章4.4.1节给出了一个程序示 ... -
多线程中的long和double
2012-12-08 19:26 4215在看一些代码的时候,会发现在定义long型和double型的 ... -
多线程中共享对象的可见性
2012-12-08 18:52 5174在阅读《java并发编程实战》的第三章的时候,看到书中的一个 ... -
一个快速、轻量级 Collection 库 Trove
2012-12-07 09:35 3789Trove一个快速、轻量级针对java原子类型(byte,i ... -
java 中的Unsafe
2012-12-05 22:25 32787在阅读AtomicInteger的源码时,看到了这个类:su ... -
在ubuntu10上编译Thrift0.8.0
2012-08-01 15:34 2169下载thrift0.8.0 ,地址: http:// ... -
生活小工具--记账小助手1.0发布
2012-05-31 15:08 1780因为自己平时喜欢记账,把每日的消费情况都记录下来,所以希望找 ... -
话费速查升级版v1.3发布
2012-05-04 12:47 1461前段时间开发了一个话费速查的小应用,最近一直保持每周更新一个 ... -
编译zeromq的java绑定:jzmq
2012-05-03 22:47 135601、 下载zeromq源码:http://www.ze ... -
Android软件包静默安装小应用 - 附源码
2012-04-21 20:50 12462老早之前,写了一个android软件包静默安装的应用,放在工 ... -
最方便的联通话费,余额查询软件来了! --- 联通话费速查v1.2
2012-04-12 18:01 4059软件介绍: 联通话费速查是一款针对联通 ...
相关推荐
ASM1153E是一款广泛应用在电子设备中的串行ATA(SATA)桥接芯片,它主要用于将SATA接口转换为其他类型的接口,如PCI-E,USB等,以便于数据传输和存储。固件刷写是更新该芯片内嵌软件的过程,以改善性能、修复错误或...
总结来说,这个主题涵盖了硬件组件(ASM1153E芯片)、外设优化(关闭休眠)、以及用户操作指南,是关于提升硬盘盒性能和效率的一个实践性教程。如果你需要关闭硬盘盒的休眠功能,这个工具和教程将非常有用。在操作前...
标题中的“asm.rar_asm电机是什么_步进电机_步进电机调速”表明这是一个关于ASM电机,...ASM电机作为一个特定的电机类型,其调速技术和应用可能有其独特之处,深入学习相关资料能帮助我们掌握其工作原理和优化方法。
1. C32ASM.chm:这是帮助文档,通常包含软件的使用指南、功能介绍和常见问题解答。用户可以通过这个文件快速学习如何使用C32Asm.exe。 2. C32asm.dll:这是一个动态链接库文件,包含了C32Asm.exe运行所需的部分功能...
2. **调试功能**:C32Asm编辑器内置了调试工具,允许用户设置断点、单步执行、查看寄存器状态和内存内容,这对于理解程序运行过程和查找错误至关重要。 3. **反汇编和二进制查看**:编辑器可能包含反汇编器功能,...
### 训练ASM英文文档知识点概述 #### 一、引言 - **Stasm**:一个C++软件包,主要用于定位面部图像中的特征点(即面部标志)。用户提供一张人脸图像,该软件能够返回这些特征点的位置。 - **适用场景**:Stasm主要...
ASM汇编语言全集是一份宝贵的资源,涵盖了汇编语言的各个方面,对于学习和深入理解计算机底层原理极其有用。汇编语言是计算机编程的一种低级语言,它与机器语言紧密相关,每一行汇编指令几乎都能直接转换为特定的...
### 将SAP数据库迁移至Oracle 11g R2 ASM的最佳实践指南 #### 概述 本指南旨在提供一个全面的技术文档,介绍如何将现有的SAP数据库迁移至Oracle 11g R2 ASM(自动存储管理)环境,并强调最佳实践以确保数据完整性和...
- `C32ASM.chm`:这通常是一个CHM(Compiled Help Manual)文件,即编译后的帮助文档,包含C32Asm的使用指南和参考信息。 - `C32asm.dll`:动态链接库文件,可能包含了C32Asm运行时需要的一些函数或服务。 - `C32...
描述中提到的“汉化了的,好用”,意味着这个win32asm工具已经被翻译成了中文,这对于中文用户来说是一个极大的便利,因为它消除了语言障碍,使得不懂英文的开发者也能轻松使用。通常,反汇编器的界面和文档都是英文...
《MCS51_ASM_IDE_2812:全面解析单片机开发的强大工具》 在电子工程和嵌入式系统的世界里,MCS51系列单片机以其高效能和广泛应用而闻名。为了更好地支持这些单片机的开发工作,出现了"MCS51_ASM_IDE_2812"这样的...
2. 在PC机和实验系统联机状态下,新建实验程序,编辑完成后进行保存(保存后缀为.asm 文件)。 3. 编译下载。 4. 全速运行,运行程序。 5. 按 RST 键退出。 七、实验程序 实验程序主要包括两个部分:数据段和代码...
- **视图V$ASM_DISK**:列出ASM实例中的所有磁盘及其状态。 - **常用方法** - **如何确定ASM实例的编号**:解释如何查找ASM实例的标识符。 - **查询DG-RAW-磁盘的对应关系**:查询diskgroup与磁盘之间的关联。 ...
标题中的“ds18.rar_DS18B20 ASM_ds18”暗示了这个压缩包主要涉及DS18B20数字温度传感器的程序代码,其中可能包含了使用汇编语言(ASM)编写的代码。DS18B20是一款单线数字温度传感器,由达拉斯半导体(现为Maxim ...
### ARM编译器参考指南知识点总结 #### 一、ARM RealView 编译工具 (RVCT) 概述 **1.1 关于ARM编译器** - **RealView 编译工具 (RVCT)**:这是一套由ARM公司开发的专业编译工具,主要用于ARM架构下的软件开发。 -...
【MCS51_ASM_IDE】是一款专为51系列微控制器设计的汇编语言集成开发环境(IDE),它提供了全面的功能,使开发者能够高效地编写、编译和调试51汇编代码。51汇编器是这个IDE的核心部分,它支持8051、8052等51家族的微...
这份OTIS电梯系统的文档对于工程技术人员来说至关重要,因为它不仅提供了详细的调试指南,还包含了必要的软件基础知识和服务工具的操作方法。熟练掌握这些内容能够帮助技术人员更好地完成现场调试任务,确保电梯系统...
《Intel® 64 and IA-32 Architectures Software Developer’s Manual》是Intel公司为开发者提供的权威指南,详细阐述了Intel 64(也称为x86-64)和IA-32架构的硬件特性、指令集以及编程模型。这份手册对于深入理解...