java Class和加载机制精华一页纸

Java 是一个解释型语言(使用了JIT后, 也有变成本地机器码的, 但一般意义上都是先预编译成字节码, 解释执行),那字节码里面包含了那些?解释的过程如何?

1、Class 字节码

I、基本信息

Class识别、版本信息

II、常量池

类似TLV表述的结构,数据类型主要是用 U1、U2 ... (对应 1,2,4,8个字节)的无符号数。

常量池并不是指类里面的常量,而是类里面所有名称和限定符(字面常量、编译常量)

因为,class中用来描述常量长度是 u2类型,即最大只能描述65535,所以java中变量名最大只能到 65535

常用的特殊类型

对象类型是用 字符 L

数组类型使用 [

比如 java.lang.String[] 则表示为[Ljava/lang/String

III、各种表集合

字段表 - 域变量 (类型、操作指令、行数等)

方法表 - 类方法 (类型、操作指令、行数等)

指令用u1 类型来标识,那说明最多智能有 256条指令;code_length使用 u4 类型,理论上可以有4294967295条指令,实际上java只允许最多 65535条指令,否则编译失败(以后不知道会否放开),因为这个限制在一些超长的方法(比如复杂jsp编译后的java),会编译失败

如果子类没有重写父类的字段和方法,就不会在子类的class文件中出现父类的参数和方法。

javap -verbose XXXClass 可以反编字节码

2、Class 加载和解析

I、加载过程

字节码的加载流程: 装载 - 链接(验证 - 准备 - 解析) - 初始化

装载(load):查找并加载二进制数据

链接(Link)- 在这一阶段,主要是 验证:确保加载类的正确性;准备:为静态变量分配内存,并初始化默认值;解析:把类的符号引用转换成直接引用

初始化:类的初始化发生在(I、创建类实例new;II、调用类的静态方法和变量;III、反射调用class.forName;IV、初始化子类;V、JVM启动时标明的启动类)

II、加载机制

Java的 类加载机制:双亲委派模型

Java的类加载是一个委托模型,三级加载,双亲委派模式

System(系统) – Extension(扩展) – Bootstrap(引导) 当System加载器碰到一个类时,首先委托Extension,委托Bootstrap加载,如果无法加载,才自己加载

这样有利于类的层次,也避免恶意破坏

两个类是否相同?

java判断是否是同一个类,除了检查类的名称(全路径),同时还判断该类的类加载器是否相同,如果被加载到不同的类加载器,类也是不同的。所以核心类都必须是引导加载

III、双亲委派模型的缺陷 - 机制的补充

这种单向委派的机制,对于框架设计有个严重的问题, 框架只设计接口, 实现由具体应用完成。

现在,子类加载器加载的类 知道父类,父类加载器加载的类不知道子类, 这样就无法完成闭环。

比如a、Java规范提供了很多接口,比如JDBC,需要具体的厂商来实现,这时候加载;b、典型的Java Servlet规范,要求每个Web应用使用自己的加载类

解决方法就是,线程上下文加载器;

通过类java.lang.Thread getContextClassLoader()和setContextClassLoader() 来获取和设置线程的上下文加载器。创建线程时, 如果未设置上下文加载器,从父线程继承一个线程上下文加载器,默认的是 系统 上下文加载器

这样 父类加载器 发现无法加载类时,可以通过 获取线程上下文加载器(即子类加载器),请求子类加载器去加载代码

线程上下文加载器 VS 当前加载器 如何选

a、默认使用当前加载器,除非有 父依赖子的情况

b、更好的策略 是 当前加载器和线程上下文加载器谁是子,就使用谁

何时需要关注类加载器?

当我们使用 反射\动态代理 等需要加载类功能的时候,背后就是在使用类加载器。特别是Web应用,比如tomcat,每个web应用都有一个对应的类加载器实例。该类加载器首先会加载应用,找不到再代理给父类加载器。这与一般类加载器是反的,也是 Java Servlet 规范推荐的,目的是Web应用的优先级高于Web容器的优先级(不加载核心库)

tomcat的classloader怎么隔离类

Tomcat的类加载机制

JDK默认的加载器

common类加载器 - CommonClassLoader (公用部分)

Catalina类加载器 CatalinaClassLoader (服务器部分) Shared类加载器 SharedClassLoader(应用部分)

WebApp类加载器 WebappClassLoader

Jsp类加载器 JsperLoader

其中CatinaClassLoader用来加载 tomcat自己的类,而WebAppClassLoader每个应用都有一个自己的类加载器

在Context容器(对应一个web应用)启动时,就会启动一个WebAppClassLoader,所以每个应用互不干涉

IV、如何自定义 类加载器

最佳的方式,利用已有的加载器,做一些扩展,比如 URL 的加载器 URLClassLoader

或者自己实现 ClassLoader类,重点是 实现 findClass 读取字节码,并调用 defineClass转换为Class 对象

V、加载即其他

利用类加载的灵活机制,有很多衍生的技术

动态代理

asm

cglib

OSGi

...

如果加载的类是确定的,当类特别多的话,如果需要加快加载时间,可以关闭加载验证过程 –Xverify:none;

VI、初始化顺序的问题

a、类加载时, 首先加载静态变量和静态方法

b、因为子类不持有父类的 属性,所以直接通过子类引用父类静态属性,不会触发子类初始化

c、构成初始化的几个条件,比如new

父类静态 > 子类静态 > 父类构造 > 子类构造

加载(静态)优先, 长辈优先

3、Class执行 - 多态实现的关键

有些方法(静态方法、私有方法等等)在解析时,就能确定方法的入口指针(直接引用),但其他方法, 可能存在override, 还有 overload的一些情况。

public class Dispatch {

static class QQ{}

static class _360{}

public static class Father{

public void hardChoice(QQ arg){

System.out.println("father choose qq");

}

public void hardChoice(_360 arg){

System.out.println("father choose 360");

}

}

public static class Son extends Father{

public void hardChoice(QQ arg){

System.out.println("son choose qq");

}

public void hardChoice(_360 arg){

System.out.println("son choose 360");

}

}

public static void main(String[] args) {

Father father = new Father();

Father son = new Son();

father.hardChoice(new _360());

son.hardChoice(new QQ());

}

}

public static void main(java.lang.String[]);

Code:

Stack=3, Locals=3, Args_size=1

0: new #16; //class ch08/Dispatch$Father

3: dup

4: invokespecial #18; //Method ch08/Dispatch$Father."":()V

7: astore_1

8: new #19; //class ch08/Dispatch$Son

11: dup

12: invokespecial #21; //Method ch08/Dispatch$Son."":()V

15: astore_2

16: aload_1

17: new #22; //class ch08/Dispatch$_360

20: dup

21: invokespecial #24; //Method ch08/Dispatch$_360."":()V

24:invokevirtual #25; //Method ch08/Dispatch$Father.hardChoice:(Lch08/Dispatch$_360;)V

27: aload_2

28: new #29; //class ch08/Dispatch$QQ

31: dup

32: invokespecial #31; //Method ch08/Dispatch$QQ."":()V

35:invokevirtual #32; //Method ch08/Dispatch$Father.hardChoice:(Lch08/Dispatch$QQ;)V

38: return

LineNumberTable:

line 30: 0

line 31: 8

line 32: 16

line 33: 27

line 34: 38

LocalVariableTable:

Start Length Slot Name Signature

0 39 0 args [Ljava/lang/String;

8 31 1 father Lch08/Dispatch$Father;

16 23 2 son Lch08/Dispatch$Father;

}

overload通过指定参数类型,编译时指向时有对应的参数类型,执行时通过不同的参数类型,就能找到具体的方法,称为静态分派

override编译出来的类型,都是相同的 类型, JVM 在运行时提供一个虚拟方法表, 如果子类改写了父类,则方法入口地址为子类方法地址,这样就能识别指向查找的方法了, 称为动态分派

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容