一、JVM布局
- 堆(线程共享)
new object()所有的对象都是存在此区域,此区域也是JVM中最大的一块区域,JVM垃圾回收站就是针对此区域
堆会被划分为:
新生代:第一次创建的对象都会分配到此区域
(a)Eden:80%的内存
(b)S0:10%的内存
(c)S1:10%的内存
老年代:经历了一定的垃圾回收之后,依然存活下来的对象会移动到老年代;大对象在创建时也会直接进入老年代。HotSpot的默认执行次数是15次,经历15次GC就会被移动到老年代
为什么大对象会直接进入老年代?
核心原因是因为大对象的初始化比较耗时,如果频繁的创建和消耗会带来一定的
性能开销,因此嘴好的实现方式是将它存入GC频率更低的老年代
- JVM栈(线程私有)
(a)局部变量:8大基础数据类型,对象的引用
(b)操作栈:每个方法都会对应一个操作栈
(c)动态连接:指向常量池的方法引用
(d)方法返回地址:PC寄存器的地址 - 本地方法栈(线程私有)
它与JVM栈比较相似,只不过JVM栈是给java和JVM使用的,而本地方法栈是为本地方法(c/c++)服务。 - 程序计数器(线程私有)
用来记录线程执行的序号 - 元空间(线程共享)(JDK1.8) 方法区(JDK1.7)
常量池、方法元信息
二、JVM类加载机制
- 加载
(a)根据类的全限定名加载二进制流
(b)将这个字节流所代表的静态存储结构转化为方法区运行的数据结构
(c)在内存中生成一个此类的方法入口 - 链接
(a)校验:对文件格式进行校验、对关键字进行校验
(b)准备:将类中定义的变量分配到内存中并设置初始值
(c)解析:初始化final修饰的常量 - 初始化:开始将执行权从JVM转移到自己写的程序,执行构造函数
- 使用
- 卸载
三、Java双亲委派模型
当加载一个类的时候,那么这个类不会直接加载,而是将这个加载任务直接交给父类,当找不到父类的时候才自己尝试去加载。
优点:
唯一性(父类执行加载一次)
安全性(会往上找而上层的类是系统提供的类,避免加载自定义的类,从而一定程度上保证了安全性)
四、垃圾回收
- 判别死亡对象
(a)引用计数器算法
给每个对象创建一个计数器,当有程序引用此类的时候计数器+1,不使用的时候计数器-1,当计数器为0是则表示此对象没人引用,就可将其归为死亡对象,等待垃圾回收器的回收。但该算法有一个缺点:不能解决循环引用的问题。
(b)可达性分析算法
按照对象的引用链一直往下找,根据引用链上是否有GC Roots判断引用的对象是否为存活的对象,如果根对象不是GC Roots那就是死亡对象。
GC Roots:
虚拟机栈中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中引用的对象
- 垃圾回收算法
(a)标记清除算法:使用可达性分析算法标记出存活的对象和死亡的对象然后清除,但是会带来内存碎片的问题。
(b)复制算法:性能比较高但是内存利用率低,新生代一般会使用此算法。
(c)标记整理算法:不会产生内存碎片,老生代一般会使用此算法。 - 垃圾回收器
(a)Serial收集器
(b)ParNew收集器
(c) Parallel Scavenge收集器(并发垃圾回收器,以吞吐量作为主要依据进行垃圾回收,适用于后端系统)
(d) Serial Old收集器
(e)Parallel Old收集器
(f)CMS收集器(更少的STW,适用于BS系统)
(g)G1收集器(STW更短,jdk11默认的的垃圾回收)