-
因为跨平台的设计。java的指令集都是根据栈来设计的(栈可以0地址,不依赖硬件(跨平台)、指令集小、指令多;执行性能比寄存器差)。
-
JIT寻找热点代码缓存,执行效率高。
-
类的加载过程:
1)加载。生成字节码文件,最后会生成代表这个类的java.lang.class对象,作为方法区内存这个类的各种数据的访问入口;
2)链接:
①验证:保证class文件的字节流中包含信息符合要求;
②准备:为变量分配内存,为它分配该类型的默认初始值,不包含final修饰的static,不会为实例变量分配初始化;
③解析:将符号引用(用符号代表的一些结构,可以节省空间)转为直接引用;
3)初始化;执行类构造器方法()的过程,顺序执行(为类的所有变量赋值和静态代码块合并得到,没有静态变量就没有); -
类加载器:引导类加载器和自定义类加载器(所有继承classLoader的都算自定义加载器),对于用户自定义类的加载器,默认使用系统类加载器进行加载;String类使用引导类加载器进行加载, java核心类库都是使用引导类进行加载的;
扩展类加载器:用户如果将自己的jar包放在jdk中的jre/lib/ext子目录下,也会自动由扩展类加载器加载。
应用程序类加载器:该类加载的是程序中默认的类加载器。 -
自定义加载器怎么写,如果不加密就继承URLClassLoader,要加密就继承ClassLoader根据路径名,将文件以二进制读进来解码,然后返回defineClass.
-
获取ClasserLoader的4种途径:
-
双亲委派机制,防止恶意攻击,类加载器收到类加载请求,并不会自己先去加载,而是把这个请求委托给父类加载器去执行;如果父类加载器还存在其父类加载器,则进一步向上委托,一次递归,请求最终达到顶层的启动类加载器。父类如果不能完成加载,子类再去完成。优势:避免类的重复加载;保护程序安全,防止核心API被串改。
-
沙箱安全机制:保证对java核心源代码的保护。
-
java对类的使用方式 :主动使用和被动使用;
-
PC寄存器(程序计数器):用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令。反编译之后的左边的数字就是指令地址(偏移地址)。
-
栈是运行时的单位,堆是存储的单位。栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
-
GC和OOM来区分回收问题。
-
虚拟机栈的大小可以是动态的或者是固定不变的。(报两种异常:前者StackOverflowError;后者OutOfMemoryError),修改栈的大小,在run的edit里面。
-
栈中的数据都是以栈帧的格式存在的;线程上每一个方法都对应一个栈帧;栈帧是一个内存区,块维系着方法执行过程中的各种数据信息。
-
方法结束的两种方式:正常结束;未捕获的异常,以抛出异常结束。
-
每个栈帧中存储着:局部变量表、操作数栈、动态链接、方法返回地址和一些附加信息。
-
局部变量表:定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,不存在安全问题,大小在编译期确定下来的。最基本的存储单位是slot(变量槽),32位以内类型占一个槽,64位的占两个。可以重复利用槽位,节省资源。
-
变量的分类:按照数据类型:1、基本数据类型;2、引用数据类型
按照在类中声明的位置分:
1、成员变量:在使用前,都经历初始化赋值;类变量:linking的prepare阶段:给类变量赋值;实例变量:随着对象的创建会在堆空间中分配实例变量空间,并进行默认赋值。
2、局部变量:在使用前必须进行显式赋值,否则编译不通过。 -
操作数栈,主要用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。
- 为什么不将架构更换为基于寄存器的架构?
答:因为想要他的跨平台性和可移植性。 - 为什么要自定义加载器?
答:隔离加载类(防止jar包路径一样导致冲突);修改类加载方式;扩展加载源;防止源码泄露。 - 使用PC寄存器存储字节码指令地址有什么用呢?
答:因为CPU需要不停的切换各个线程,切换回来之后,就得知道接着从哪开始继续执行。 - 为什么使用PC寄存器记录当前线程的执行地址呢?
答:通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。 - PC寄存器为什么会被设定为线程私有的?
答:公用会导致多个线程之间切换时互相覆盖记录的当前的执行行,导致执行出错,因此为每个线程都分配单独的一个最好。 - java虚拟机栈是什么?
答:每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应一次次的java方法调用;是线程私有的。 生命周期和线程一致。



