Java学习笔记 - Java中的编译和反编译

下面的这个图片,是本文的思维导图,用于快速了解Java中的编译和反编译的相关知识。不过图片稍大,建议在新标签页中打开。如果觉得图片看得累,可以直接阅读文章,会比思维导图更加详细。

Java中的编译和反编译思维导图

阅读之前 ,问一个小问题

你知道什么是编程语言吗?之所以问这个问题,是因为编译和反编译,其实质就是语言间的翻译转换。

一、什么是编程语言(Programming Language)

  编程语言分低级语言(Low-level Language)和 高级语言(High-level Language)两种。

1. 低级语言:直接用计算机指令编写程序。如机器语言(Machine Language)、汇编语言(Assembly Language)。
2. 高级语言:用语句编写程序,语句是计算机程序的抽象表示。如C、C++、Java等。

二、什么是编译(Compiler)

简单来说,编译就是将高级语言翻译成低级语言。


1. 概念:
  编译就是将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻译成计算机能解读、运行的低阶机器语言的程序的过程。负责这一过程的处理的工具,叫做编译器。不同的语言都有自己的编译器,Java语言中负责编译的编译器是一个命令:javac 。

2. 步骤:

编译分为 前端编译 和 后端编译 两个部分。简单来说,前端编译的任务是:将Java源程序编译为Class文件;后端编译的任务是:由JVM的解释器 解释执行Class文件。

Java中的编译过程

词法分析

  从左到右一个字符一个字符地读入源程序,将字符序列转换为标记(token)序列。这里的标记是一个字符串,是构成源代码的最小单位。在这个过程中,词法分析器还会对标记进行分类。规序列范的标记包含有 :1. java关键词:package、import、public、class、int等; 2. 自定义单词:包名、类名、变量名、方法名; 3. 符号:=、;、+、-、*、/、%、{、}等。此阶段不会检查代码的结构是否正确。

语法分析

  在词法分析的基础上将单词序列组合成各类语法短语,如“程序”、“语句”、“表达式”等等。语法分析程序判断源程序在结构上是否正确,此阶段不会检查代码的上下文逻辑是否正确。

语义分析

  语义分析是编译过程的一个逻辑阶段,在这一阶段,语义分析器会将复杂语法转换成简单语法(如将forearch转换成for);对结构上正确的源程序进行上下文有关性质的审查,审查源程序有无语义错误、类型错误(如用小数表示数组下标,程序会报错)等,为代码生成阶段收集类型信息;还会添加一些代码,如添加默认构造器。其具体流程如下:

  1. 添加默认的无参构造器(在没有指定任何有参构造器的情况下);把引用其他类的方法或者变量,抑或是继承实现来的变量和方法等 输入到类自身的符号表中。
  2. 处理注解。
  3. 标注:检查语义合法性、进行逻辑判断。
    • 检查语法树中的变量类型是否匹配(如 String s = 1 + 2; //这样”=”两端的类型就不匹配)。
    • 检查变量、方法或者类的访问是否合法(如 一个类无法访问另一个类的private方法)。
    • 变量在使用前是否已经声明、是否初始化。
    • 常量折叠(如 String s = “hello” + “world”,语义分析后String s = “helloworld”)。
    • 推导泛型方法的参数类型。
  4. 数据流分析。
    • 变量的确定性赋值(如 有返回值的方法必须确定有返回值)
    • final变量只能赋一次值,在编译的时候再赋值的话会报错。
    • 所有的检查型异常是否抛出或捕获。
    • 所有的语句都要被执行到(如 在return后边的语句就不会被执行到,除了finally块)。
  5. 进一步语义分析。
    • 去掉永假代码(如 if(false) )。
    • 变量自动转换(如 int和Integer)自动装箱拆箱。
    • 去掉语法糖(如 foreach转化为for循环、assert转化为if、内部类解析成一个与外部类相关联的外部类)。
  6. 将上述语法树转换成注解语法树。
中间代码生成

  生成一个明确的低级的或类机器语言的中间表示。性质: 1.易于生成; 2.能够轻松地翻译为目标机器上的语言。(如Class文件)。

Java中的后端编译

JVM运行原理


1. 传统JVM解释器
概念:通过 javac 将程序源代码编译转换成 java 字节码后,JVM解释器将字节码逐条翻译成对应的机器指令。
缺点:其执行速度比可执行的二进制字节码程序慢很多。
改进:引入JIT技术。

2. JIT即时编译器(Just In Time Compiler)
  当JVM发现某个方法或代码块执行得特别频繁时,就会认为这是“热点代码(Hot Spot Code)”。然后虚拟机会将部分“热点代码”翻译成本地机器相关的机器码,并进行优化和缓存,以备下次使用。完成这个任务的就是即时编译器。

  HotSpot虚拟机中内置了两个JIT编译器:Client Complier和Server Complier,分别用在客户端和服务端,目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器直接配合的方式工作。值得注意的是,即时编译器并非虚拟机必须的部分。

  当 JVM 执行代码时,它并不立即开始编译代码。首先,如果这段代码本身在将来只会被执行一次,那么从本质上看,编译就是在浪费精力。因为由解释器直接解释执行这段代码,无疑比先编译这段代码然后再执行,要快很多。而这也是为什么主流的虚拟机都是采用解释器和编译器共存的架构;第二个原因是最优化,当 JVM 执行某一方法或遍历循环的次数越多,就会更加了解代码结构,那么 JVM 在编译代码的时候就做出相应的优化。

3. 热点探测
  前面说过,触发JIT编译的条件就是,一段代码被认为是热点代码。那么如何识别出一段代码是热点代码呢?目前主要是采用热点探测的方式,热点探测方式主要有以下两种:

  • 基于采样的方式探测:周期性地检查栈顶,如果发些某个方法经常出现在栈顶,则认为这是个热点方法。虽然简单,但是容易受线程阻塞或其他因素的影响。
  • 基于计数器的热点探测:为每个方法,甚至是代码块建立计数器,以统计方法的执行次数。当某个方法的执行次数超过阀值时,就认为是热点方法,触发JIT编译。在HotSpot虚拟机中使用的就是这种方式。它为每个方法准备了两个计数器:方法调用计数器(记录方法被调用的次数)和回边计数器(记录方法中的for或者while的运行次数)。

  注意:javac 命令生成的class文件,是JVM可以识别的文件,但仍不是机器能够识别的语言。机器只能识别机器语言。因此,还需要JVM再将class文件类型字节码转换成机器可以识别的机器语言。

三、 什么是反编译


1. 概念

  将已经编译好的编程语言还原到未编译的状态,即找出程序语言的源代码。Java语言中的反编译一般指将class文件转换成java文件。


2. 工具:javap、jad、cfr

  • javap:jdk自带,可以查看字节码,但是不能反编译成java文件。使用命令:javap -c fileName.class
  • jad:使用简单。使用命令:jad switchDemoString.class
  • cfr:功能多,能适用各种不同场景。使用命令:java -jar cfr_0_125.jar switchDemoString.class –decodestringswitch false。其余命令也可用 java -jar cfr_0_125.jar –help 查看。


3. 如何防止反编译

  • 隔离Java程序,即不让别人接触到你的Class文件。
  • 加密Class文件。
  • 代码混淆,即将代码转换成功能等价,但是难以阅读的形式。

五、 参考文章
深入分析Java的编译原理
Javac编译原理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值