JDK、JRE、JVM的区别:
JVM:java visual machine,java虚拟机,整个java实现跨平台的核心,运行class文件
JRE:java runtime environment 包含JVM,以及一些核心类库
JDK:java development kit,JRE+一些工具包
JVM的组成以及作用
类加载系统:加载------》链接(验证、准备、解析)-----》初始化,将编译后的.class文件加载到JVM系统。
方法区:线程共享的,也是GC的区域。存放类加载后的版本信息、运行时常量池、域信息、接口信息、方法信息。注意:1.8以后的静态变量、字符串常量池已经放在了堆。方法区的实现1.8之前是永久代,1.8以后叫元空间,从堆内存迁移到了本地内存。
堆:线程共享的,也是GC的区域。存放对象以及数组、字符串常量池、静态变量。
虚拟机栈:线程私有的,不存在GC,包含局部变量表、操作数栈、方法出口、动态链接,每执行一个方法会压入一个栈帧,方法执行完会弹栈。
本地方法栈:线程私有的,不存在GC,虚拟机栈执行的是java方法,而本地方法栈执行的是native方法(C语言)
程序计数器:线程私有的,不存在GC,也没有OOM发生,记录的是下一条指令的执行地址。
执行引擎:包含解释器、JIT编译器、GC,负责执行字节码指令以及垃圾回收。
类加载子系统:
类加载器:用户自定义加载器 User ClassLoader----》程序/系统加载器----Application/System ClassLoader (加载类路径文件)》扩展类加载器 extentionClassLoader-----(加载jir/lib/ext包下的文件)》根加载器 (加载jre/lib/rt.jar)BoostrapClassLoader
加载过程:
- 加载:javac 命令后的会得出.class文件,将.class二进制文件加载到jvm内存。
- 连接: 验证:验证文件的安全性,例如是否是cafe baby魔数开头 class的版本是否被使用的jvm兼容。准备:给类中的静态变量赋初始值,分配空间。 解析:将常量池中的符号引用转为直接引用。
- 初始化:如果有静态变量,则执行clinit方法,给静态变量显式赋值。
双亲委派:
是什么?:加载类时,自己不会去加载,而是交由自己的上一级加载器去加载。如:我们写的程序一般是通过程序加载器去加载的,而程序加载器不会先去加载,而去找他的上一级扩展类加载器加载,扩展类加载器也不会加载,而找到最顶层的根加载器。根加载器尝试加载,如果能加载直接返回,加载不了又会传给下一级。
作用:1、确定类只能被加载一次 2、保护了核心类库的安全性
实现类加载器:继承ClassLoader类,重写其findClass()/loadClass()(官方建议)
类的缓存机制:
修改class文件后必须重启JVM。加载后的class文件会存放在缓存区,jvm每次读的时候会优先读取缓存区的数据,而变更后的文件要想见效,必须重新加载进缓存区。
JVM判断两个对象相等的条件:1、全类名相同 2、同一类加载器、
JVM的运行时包:由同一类加载器加载的相同包下的类组成了运行时包。只有属于同一运行时包的类才可互相访问(默认访问级别)
类的初始化顺序:
- 父类的静态变量、静态代码块(静态变量与静态代码块按顺序)
- 子类的静态变量、静态代码块(静态变量与静态代码块按顺序)
- 父类的成员变量
- 父类的构造方法
- 子类的成员变量
- 子类的构造方法
类的主动使用:
- new 关键字实例化
- 读取一个类的静态字段
- 调用类的静态方法
- 采用反射对类型进行反射调用
- 类初始化时,触发父类初始化
- main方法启动,触发
- jdk8新加入的默认方法,如果这个类接口的实现类初始化,那么该接口页必须初始化
类的被动使用:
除了以上主动使用,都是被动使用。
例如:
- 子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义来引用类,不会触发此类初始化。
- 引用常量,常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的初始化
栈
什么情况会产生栈溢出?
1、线程请求的栈深度大于JVM的栈深度
2、栈支持动态扩展,动态扩展时,无法申请足够的内存。
栈和堆的区别?
1、栈的物理地址是连续的,堆的物理地址是不连续的
2、栈是运行时单位,局部变量表存储的是基本数据类型的指,引用数据类型的引用,操作数栈、方法出口
3、栈是线程私有的,栈溢出抛出的异常是stackOverFlow,堆是线程共享的,堆抛出的异常是heap outOfMemory
4、栈的空间大小远远小于堆
堆
新生代:空间默认占1/3堆空间,分伊甸园区Eden、幸存者0区Survior 0(From)、幸存者1区Survior 1(To),新生代的垃圾收集叫Minor GC。
老年代:存放生命周期比较长的对象
创建对象的几种方式:
- new创建实例
- 反射Class.newInstance/Constructor.new Instance()
- 反序列化
- clone() 注意:此方式没有调用构造方法
创建对象的流程:
- 检查类是否被加载,没有则加载
- 为对象分配内存,内存规整,用指针碰撞,否则用空闲列表
- 并发处理,默认采用TLAB,对象太大,采用CAS+重试
- 初始化内存空间(赋默认值)
- 设置对象头信息(类的元数据、hashCode、GC信息、锁信息)
- 显式初始化/代码块中初始化----->执行构造器方法
对象的内存布局
- 对象头 包含类型指针(指向方法区的类元信息)和运行时元数据(hashcode、分代年龄、锁状态等)
- 实例数据 (父类的实例数据、自身的实例数据)
- 对齐填充 主要为了对齐
对象访问的模式
句柄池:栈引用指向句柄池,句柄池保存实例数据指针、类型指针。实例数据指针指向实例数据,类型指针,指向方法区的类数据。优点:对象移动,无需改变栈指针。
直接指针:栈引用指向实例数据,实例数据里面保存类型指针,类型指针指向方法区的类元数据。
优点:开销更小,定位更快