JVM空间划分
线程共享和线程私有
1.7:
线程共享:
堆、方法区
线程私有:
虚拟机栈、本地方法栈、程序计数器
本地内存
1.8:
线程共享:
堆
线程私有:
老三样
本地内存,元空间
程序计数器
虚拟机栈
由一个个栈帧组成
每一个栈帧中包含了:
- 局部变量表
- 方法返回地址
每调用一次函数,就会有对应的一个栈帧入栈。
本地方法栈
同虚拟机栈,只不过存的是native方法
native本地方法,就是指非java语言实现的,而是更底层的用C实现的
堆
用来存放对象的
新生代、老年代、永久代(元空间)
字符串常量池
方法区
是一个抽象的概念
具体实现由:永久代、元空间实现
用来存放类信息的地方、以及运行时常量池
直接内存
独立于JVM之外的,只受操作系统影响的内存
垃圾回收原理
- 先执行young GC(一般使用 标记复制法)
- 然后执行full GC(标记清除、或者标记整理)
对象创建过程
- 类加载检查
- 分配内存
- CAS失败重试
- TLAB空间预留
- 初始化0值
- 设置对象头
- 初始化init
对象的内存布局
- 对象头
- 类指针
- GC年龄
- Mark word锁信息
- 实例数据
- 对其填充
对象的访问定位
两种:
句柄
直接指针
GC原理
垃圾回收的基本原理
- 优先在新生代分配,回收Minor GC
- 当老年代满了的时候,会触发full gc
- 什么情况下新生代对象加入老年代:年龄够了,大文件直接进入
对象死亡的判断方法
引用计数法,一般不用,有循环依赖问题
可达性分析法:
初次标记GC ROOT,
并发标记之后的,
最后STW重新。
GC root有哪些?
虚拟机站、本地方法栈、方法区的常量引用的对象
常量的GC规则
常量本质上是一种弱引用,
当不再有虚拟机栈的变量引用它时,下一次GC就会回收。
类信息的回收原则
也是一种弱引用,它的引用前提比较多
- 没有实例了
- 没有类加载器了
- 没有相关类的引用了
垃圾收集算法
年轻代的复制标记算法为什么是8 :1 :1?
分代的3种垃圾收集
1、Serial + SerialOld
单线程,STW
2、Parallel Scavenge + Parallel Old
多线程并行,STW
默认1.8
3、ParNew + CMS
1、CMS的特点:第一个并发的垃圾收集器
2、原理:可达性分析+ 三色标记
3、过程:3此标记,1次并发整理
4、解决漏标:使用增量更新
5、缺点:浮动垃圾+标记清除碎片太多
分区收集器
4、G1
1、第一个分区收集器,1.9开始默认、分区,可以设置最大停顿时间,
2、原理:3个回收过程
young GC、Mix GC、full GC
3、过程:3次标记+1次筛选清理(用优先队列维护需要先被清理的内容)(STW)
4、解决漏标:STAB(需要额外的RSET,记录当前的Reign被哪些别的Reign给引用过)
5、采用标记整理、标记复制
5、ZGC收集器
颜色指针+读屏障
类文件结构
类文件以.class存在在方法区(元空间)
总体构成:略
类加载过程
1、加载
- 类加载器读取字节码
- 生成class类,作为访问方法区中类文件结构的入口
2、连接
2-1、验证
主要是验证字节码,保证字节码符合JVM规范
2-2、准备
分配内存,初始化零值
2-3、解析
把符号引用转为直接引用
符号引用是指类、方法、等符号指向内存地址,是间接的。
直接引用是指,使用内存地址偏移量指向具体的内容,保证了快速访问。
3、初始化
就是执行构造方法
当实例化时才执行
注意:
按需加载、按需连接。
类加载器
介绍
1、读取解析字节码中的内容,
2、生成class对象存入方法区。
注意:
是按需加载
且加载有持久性
内置的3种类加载器
1、BootStrap ClassLoader
long包下的、Object的。。。
2、Extension ClassLoader
JDBC等
3、App ClassLoader
用户自定义的类
如何自定义类加载器
1、定义类加载器,继承ClassLoader、重写findClass或者loadClass方法,更具全类名找到字节码,然后创建class对象到方法区。
2、使用时
- new 类加载器
- 使用loadClass加载
- 使用得到的class对象,newInstance、getMethod、invoke创建实例、使用实例方法。
findClass和loadClass的区别?
- loadClass:先去父类中使用loadClass加载,如果没法加载,才会使用自己的loadClass的加载逻辑。
- findClass:不去父类中加载,直接在本层加载
loadClass遵循双亲委派机制,findClass不遵循
双亲委派机制
当一个类加载器要加载一个字节码时,首先不是自己加载,而是使用上层的类加载器加载,如果上层无法加载才使用本层加载。
- 自底向上询问
- 自顶向下加载
双亲委派模型的好处
- 保证类不会被重复加载(假设不同的类加载器加载同一个类,那么最后都会变成同一个类加载器加载类)
- 保证核心API不被篡改(核心的类,比如long下的类,最终都只会被bootstrapClassloader加载)
JVM调优
监控内存情况
1、优先可视化:
使用Jconsole
2、其次使用命令行:
Jstat、Jinfo
JVM调优参数设置
1、
分配堆内存
分配栈内存
分配元空间内存
分配新生代、老年代内存
2、
调整对象年龄阈值(什么时候适合缩短?)
默认多大对象直接进入老年区
3、设置垃圾收集器
4、G1的最大停顿时间
-Xms
-Xmx
-Xss
-XX:MetaSpace
-XX:newSize
-XX:MaxTenuringThreshold
调优思路
就是尽可能把该进入老年代的提前进入老年代,不该进入老年代的在新生代就GC掉