谈谈你对java的理解
平台无关性
GC
语言特性
面向对象
类库
异常处理
Compile Once, Run Anywhere如何实现?
通过java虚拟机
java源码首先被编译成字节码,再由的不同平台的JVM进行解析,java语言在不同的 平台上运行时不需要进行重新编译,java虚拟机在执行自己码的时候,把字节码转换成具体平台上的机器指令
为什么JVM不直接将源码解析成机器码去执行
准备工作:每次执行都需要各种检查
兼容性:也可以将别的语言解析成字节码
JVM如何加载.class文件?
Class Loader: 依据特定格式,加载Class文件到内存中
Execution Engine: 对命令进行解析
Native Interface: 融合不同开发语言的原生库为java所用
Runtime Data Area: JVM内存空间结构模型
谈谈java的方射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
一个简单的放射例子
类从编译到执行的过程:
编译器将Robot.java源文件编译为Robot.class字节码文件
ClassLoader将字节码转换为JVM中的Class<Robot>对象
JVM利用Class<Robot>对象实列化为Robot对象
谈谈ClassLoader
ClassLoader在java中有着非常重要的作用,它主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给java虚拟机进行连接,初始化等操作。
ClassLoader的种类
BootStrapClassLoader: C++编写,加载核心库java.*
ExtClassLoader: java编写,加载扩展库javax.*
AppClassLoader: java编写,加载程序所在目录
自定义ClassLoader: java编写,定制化加载
自定义ClassLoader的实现
关键函数:
protected Class<?> findClass(String name) throws ClassNotFoundException{
Throw new ClassNotFoundException(name)
}
protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError{
return defineClass(null, b, off, len, null);
}
谈谈类加载器的双亲委派机制
当一个类要加载的时候,若不考虑自定义的类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理会先检查自己是否已经加载过,如果没有再往上。直到到达Bootstrap classLoader之前,都是没有哪个加载器自己选择加载的。如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
为什么要使用双亲委派机制去加载类
避免多份同样字节码加载
类的加载方式
隐式加载:new
显式加载:loadClass, forName等
类的装载过程
- 加载:通过ClassLoader加载class文件字节码,生成Class对象
- 连接:
- 校验:检查加载的class的正确性和安全性
- 准备:为类变量分配存储空间并设置类变量初始值
- 解析:JVM将常量池内的符号引用转换为直接引用
3.初始化:执行类变量赋值和静态代码块
LoadClass和forName的区别
Class.forName得到的class是已经初始化完成的 (jdbc注册驱动)
Classloader.loadClass得到的class是还没有连接的 (spring延迟加载)
java内存模型:
内存简介:
地址空间的划分:
内核空间:独立于普通应用程序,运行在较高的特权级别上,它们驻留在被保护的内存空间上,拥有访问硬件设备的所有权限。
用户空间:运行在用户空间的应用程序只能看到允许它们使用的部分系统资源,并且不能使用某些特定的系统功能,也不能直接访问内核空间和硬件设备,以及其他一些具体的使用限制。
JVM内存模型——JDK8
线程独占部分:
程序计数器(Program Counter Register)
当前线程所执行的字节码行号指示器(逻辑)
改变计数器的值来选取下一条需要执行的字节码指令
和线程是一对一的关系即“线程私有”
对java方法计数,如果是native方法则计数器的值为undefined
不会发生内存泄露
Java虚拟机栈(Stack)
Java方法执行的内存模型
包含多个栈帧(栈帧存储:局部变量表,操作数栈,动态连接,返回地址)
本地方法栈(native)
与虚拟机栈相似,主要作用于标注了native的方法
局部变量表和操作数栈
局部变量表:包含方法执行过程中的所有变量
操作数栈:入栈,出栈,复制,交换,产生消费变量
递归为什么会引发java.lang.StackOverflowError异常
递归过深,栈帧数超出虚拟栈深度的(每次递归都会创建一个栈帧,而且会保存当前栈帧的引用的,过多层数会撑爆虚拟机栈)
虚拟机栈过多会引发java.lang.OutOfMemoryError异常
线程共享部分:
元空间(MetaSpace)与永久代(PermGen)的区别
都是方法区的实现
元空间使用的是本地内存,而永久代使用的是jvm内存(java.lang.OutOfMemoryError:PermGen space)
MetaSpace相比PermGen的优势
字符串常量池存在永久代中,容易出现性能问题和内存溢出
类和方法的信息大小难以确定,给永久代的大小指定带来困难
永久代会为GC带来不必要的复杂性
方便HotSpot与其他JVM如Jrockit的集成
Java堆(Heap)
对象实例的分配区域
GC管理的主要区域(新生代(Eden, survice0, survice1,老年代)
JVM内存模型常见面试题:
JVM三大性能调优参数 -Xms, -Xmx, -Xss的含义
Java -Xms128m, -Xmx128, -Xss256k -jar xxxx.jar
-Xss: 规定了每个线程虚拟机栈(堆栈)的大小
-Xms: 堆的初始值
-Xmx: 堆能达到的最大值
Java内容模型中的堆和栈的区别——内存分配策略
静态存储:编译时确定每个数据目标在运行时的存储空间需求
栈式存储:数据区需求在编译时未知,运行时模块入口前确定
堆式存储:编译时或运行时模块入口都无法确定,动态分配
联系:引用对象,数组时,栈里定义变量保存堆中目标的首地址
- 管理方式:栈自动释放,堆需要GC
- 空间大小:栈比堆小
- 碎片相关:栈产生的碎片远小于堆
- 分配方式:栈支持静态和动态分配,而堆仅支持动态分配
- 效率:栈的效率比堆高
不同JDK版本之间的intern()方法的区别——JDK6 VS JDK6+
String s = new String(“a”) ; s.intern();
JDK6:当调用intern方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串对象的引用。
JDK6+: 当调用intern方法时,如果字符串常量池中先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,如果该字符串对象已经存在java堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。