详解JVM和GC垃圾回收
JVM架构图分析
我们编写的代码是java源文件.java文件,经过编译器编译成字节码文件.class文件,通过类装载器装载进jvm内存区域。
Java虚拟机运行时数据区
程序计数器:当前线程的行号指示器,用来记录当前执行到代码的哪一行、
Java虚拟机栈:存局部变量表,操作数栈,动态链接,方法出口等
本地方法栈:与虚拟机栈一样,只不过调用native服务
Java堆:存放数组,和对象实例,该区域线程共享
方法区:也叫永久代,存类信息,常量,静态变量。在jdk1.8取消了方法区,叫原空间,直接有直接内存管理
对象的创建方式有几种?
1 new关键字 通过构造方法创建
2 Class的newInstance 构造方法
3 Constructor类的呢哇Instance 构造方法
4 clone
5 反序列化
创建对象的过程
new 判断类是否加载
分配内存 指针碰撞,空闲列表
并发处理 CAS同步
初始化
init
对象的访问定位
直接指针:直接指向对象地址
句柄访问:指向指针的指针
在java堆中会维护一个句柄池,来装句柄,当对象发生改变的时候,只需要改变句柄池中的句柄就可以
垃圾回收机制
如何判断一个对象是否可以回收
1 引用计数器法:每个对象被引用一次,该对象的计数器加一,引用完计数器减一,到0 的时候可以被垃圾回收。
问题:对象之间相互引用,永远无法被垃圾回收
2可达性分析法:通过GCRoot形成一个引用链,只要是引用链可达的对象,就不会垃圾回收
垃圾回收算法都有哪些?
1 标记清除: 先标记垃圾,然后对垃圾对象进行垃圾回收。
问题:产生内存碎片
2 标记整理: 先标记垃圾,在对垃圾进行整理,不会产生内存碎片,但是降低了回收效率
3 复制算法:当一般空间满了的时候,就会把非垃圾回收对象复制到另一半内存,然后把那一半内存垃圾回收
问题:浪费一半的内存
4分代垃圾回收
对象分代
按存周期来分成 年轻代,老年代 , 永久代
年轻代占3分一1 老年代占3分之2
年轻代分为三个区域,Eden,Survivor from ,Survivor to 默认比例是8:1:1
对象会被先分 分配到Eden区,经过一次MinorGC进入from区,from和to区交换指针,对象年龄加1 ,对象到达15岁会进入老年代。
大对象Eden放不下,会直接进入老年代
垃圾回收器
一共有7种垃圾回收器
Serial(复制算法):是年轻代单线程的垃圾回收器
ParNew(复制算法):是年轻代多线程的垃圾回收器
Parallel Scavenge(复制算法):多线程 高吞吐,高cpu
SerialOld(标记整理):单线程,Serial老年代版本
ParallelOld(标记整理):多线程,老年代
CMS(标记清除):老年代并行收集器,追求最短停顿
G1收集器(标记整理):整个堆垃圾回收器
CMS垃圾回收器
1 初始标记—》stop the world
2 并发标记 --》
3 重新标记 --》stop the world
4并发清除
G1垃圾收集器
G1将整个堆分称大小固定的独立区域(Region),跟踪这些区域的垃圾堆积程度,维护一个优先级队列,优先回收垃圾最多的区域
类加载机制
类装载方式:
1 隐式装载: new
2 显示装载: class.forName
类装载执行过程
1 加载: 找到对应的class文件
2 验证:检查加载的class文件的正确性
3准备:给类中静态变量分配内存
4 解析:将常量池中的符号引用转化成直接引用
5 初始化:静态变量,静态代码块初始化
类加载器有哪些?
1 启动类加载器
2 扩展类加载器
3 系统类加载器
4 用户自定义加载器
双亲委派模型
接到加载请求的时候,自己先不去处理,交给自己的父类去处理,最顶层父类处理不了,再逐级向下加载。
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。