一、定义
- 虚拟机栈
虚拟机栈代表是线程维度,一个线程有且只有一个虚拟栈。虚拟机栈存储的单位是栈帧,而一个栈帧包含了:局部变量表、操作数栈、动态链接、方法出口等信息。正在的字节码计算,出栈入栈可不再虚拟机栈上噢。虚拟机栈仅仅是用来保存该线程,所调用所有的方法。每次在调用方法的时候,肯定会先初始化方法的栈帧,然后入虚拟机栈。
- 操作数栈
java类的一个方法,可以看做是一个操作数栈。一定要和虚拟机栈区分开。操作数栈仅仅是虚拟机栈保存的栈帧,且还是栈帧中一小块数据而已。表示一个完成方法执行运行时栈。方法的所有字节码操作,都是在这个栈帧中出栈和入栈。
二、java源码的编译过程
- 过程
词法分析 -> 语法分析 -> 语义分析 -> 数据/控制流分析 -> 解语法糖 -> 生成字节码
- java泛型是由语法糖实现的,即是编译器实现的,不是虚拟机实现。因为,还没到虚拟机这层。虽然在编译阶段,编译器会把泛型擦除,塞上原始类型,但是泛型的类型信息还是被保存在方法code属性中。反射的时候,可以通过方法签名Signature,得到泛型的具体类型信息。比如:List<String>在编译的时候,会被擦除为List<E>,但是code属性中,通过Signature可以得到其泛型类型为String类型。
三、java方法区
https://blog.csdn.net/dshf_1/article/details/87171171
java方法区存储的类对象(非实例对象)结构,如下:
image.png
java中的所有类都是已oop-klass为设计基础的
四、破坏双亲委派模型的应用场景
4.1 JDBC为什么要破坏双亲委派模型
java rt.jar中的系统类,通过SPI加载用户路径下的实现类。是通过Thead.getContextClassLoader,来破坏双亲委派模式加载的。
比如:rt.jar中的JDBC、JNDI、JBI等框架,java只是定义规范接口,具体实现由厂商来。在应用执行到JDBC里面的代码时,发现了一个SPI接口,那么接口实现类的加载器就要从Thread取了。根据类加载机制,当被装载的类引用了另外一个类的时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类。发现当前类加载是Root加载器,根本无法加载厂商的类。
JDBC的Driver接口定义在JDK中,其实现由各个数据库的服务商来提供,比如MySQL驱动包。DriverManager 类中要加载各个实现了Driver接口的类,然后进行管理,但是DriverManager位于JAVA_HOME中jre/lib/rt.jar 包,由BootStrap类加载器加载,而其Driver接口的实现类是位于服务商提供的 Jar 包,根据类加载机制,当被装载的类引用了另外一个类的时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类。也就是说BootStrap类加载器还要去加载jar包中的Driver接口的实现类。我们知道,BootStrap类加载器默认只负责加载 JAVA_HOME中jre/lib/rt.jar 里所有的class,所以需要由子类加载器去加载Driver实现,这就破坏了双亲委派模型。
查看DriverManager类的源码,看到在使用DriverManager的时候会触发其静态代码块,调用 loadInitialDrivers() 方法,并调用ServiceLoader.load(Driver.class) 加载所有在META-INF/services/java.sql.Driver 文件里边的类到JVM内存,完成驱动的自动加载。
这个子类加载器是通过 Thread.currentThread().getContextClassLoader() 得到的线程上下文加载器。如下:
public Launcher() {
...
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
...
}
4.2 Tomcat为什么要破坏双亲委派模型
Tomcat如何破坏双亲委派模型的呢?
每个Tomcat的webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。
事实上,tomcat之所以造了一堆自己的classloader,大致是出于下面三类目的:
- 对于各个
webapp
中的class
和lib
,需要相互隔离,不能出现一个应用中加载的类库会影响另一个应用的情况,而对于许多应用,需要有共享的lib以便不浪费资源。 - 与
jvm
一样的安全性问题。使用单独的classloader
去装载tomcat
自身的类库,以免其他恶意或无意的破坏; -
热部署。相信大家一定为
tomcat
修改文件不用重启就自动重新装载类库而惊叹吧。
五、java实例字段的访问
比如有个Person类有两个字段name和age,Man继承Person,有个gender字段。那么man对象是怎么访问字段的呢?java语法上,没啥好说的,直接getName或者man.name都能访问到。那么在JVM或者内存层面,怎么访问的呢?
- 首先我们要搞清楚,man对象存储的是啥?Man man = new Man("张三",20,"男性")。这段代码在执行的时候,就是对象创建的过程。这里就不多介绍了。只要知道结果是:在内存中开辟了一段连续地址,字段顺序按照规则依次排好。内存中通过utf-16,储存的是文本二进制。为啥用utf-16,可以参考:Java 为什么使用 UTF-16 而不是更节省内存的 UTF-8
- 既然知道了man对象的数据在内存中怎么存储的,那么是不是就知道了应该怎么取出来。比如man.name,jvm根据步骤1中的顺序从内存中拿出来,然后解码后就是结果了。
网友评论