平台无关性如何实现?
javac test.java 编译得到class文件
java命令是针对字节码文件
字节码文件由特定平台的JVM进行解析,转换成特定平台的执行指令
JVM如何加载class文件
jvm是一个内存虚拟机
class文件会先进入class loader,符合class文件格式的class加载到内存中,然后进入到运行时数据区,通过execution engine对字节码文件中的命令进行解析,native interface会融合不同开发语言的原生库为Java所用。
运行时数据区即我们平时说的JVM内存空间结构模型
什么是反射
写一个反射的例子。
私有方法需要setAccessible为true
谈谈classloader
类从编译到执行的过程
编译器将Robot.Java源文件编译为class文件
classLoader将字节码转换为JVM中的Class对象
JVM利用Class对象实例化为Robot对象
种类:
BootStrapClassLoader:c++编写,加载核心库
extClassLoader:Java编写,扩展库
appClassLoader:Java编写,加载程序所在目录
自定义classloader:Java编写,定制化加载
classloader的双亲委派机制
避免多份同样字节码的加载
类的加载方式
隐式加载:new
显式加载:loadclass,forname
loadclass:不会执行初始化,懒加载方式
forname:会执行初始化,执行静态代码块
类的装载过程:
你了解Java的内存模型吗?
内核空间
用户空间
线程私有:程序计数器,虚拟机栈,本地方法栈
线程共享:metaspace,Java堆
程序计数器:
- 当前线程所执行的字节码行号指示器【逻辑】
- 改变计数器的值来选取下一条需要执行的字节码指令
- 和线程是一对一的关系,即线程私有
- 对Java方法计数,如果是native方法则计数器值为undefined
- 不会发生内存泄漏
虚拟机栈:
- Java方法执行的内存模型
- 包含多个栈帧
局部变量表和操作数栈:
java.lang.StackOverFlow:递归调用的话,可能因为,栈帧太多,超过虚拟机栈的深度限制
java.lang.OutOfMemoryError
本地方法栈
线程共享:
元空间和永久代:方法区的实现
元空间使用本地内存,而永久代使用的是jvm内存
字符串常量池存在永久代中,容易出现性能问题和内存溢出
类和方法的信息大小难以确定,给永久代的大小指定带来困难
永久代会为GC带来不必要的复杂性
Java堆:
对象实例的分配区域
GC管理的主要区域
-Xss:规定每个线程虚拟机栈大小,一般为256k,影响并发线程数
-Xms:堆的初始值,超过的话扩容到最大值
-Xmx:堆最大值,一般两个一样,因为扩容时会发生内存的抖动
堆和栈的区别
联系:引用对象、数组时,栈里定义变量保存堆中目标的地址
栈自动释放,堆需要GC
栈比堆的空间更小
栈产生的碎片远小于堆
栈支持静态和动态分配,堆支持动态分配
栈的效率比堆高
JDK6 vs JDK6+的intern()
jdk7之后方法区中的常量池已经移动到了堆中,主要原因是之前字符串常量池存在永久代中,而永久代的内存比较有限,频繁调用intern创建字符串对象,可能会在永久代中发生outOfMemory异常
false
false
false
true
Java垃圾回收机制
针对堆内存
对象被判定为垃圾的标准:没用引用它的对象
引用计数法:
两个对象实例互相引用的话,永远无法回收
可达性分析:
可以作为GCroot的对象
垃圾回收算法
标记清除算法:内存碎片
复制算法:分成两部分,存活对象转移到另一部分,解决内存碎片,浪费空间,年轻代中使用
标记整理:标记,清除垃圾对象,整理存活对象在连续区域,适用于存活率高的场景,老年代中
分代收集算法:
Minor GC:年轻代,朝生夕死的对象
对象过大或者超过15岁时,进入老年代
老年代:存放生命周期较长的对象,full GC
常见垃圾回收器
年轻代
老年代
GC相关面试题
Java中的强引用,软引用,弱引用,虚引用