文章目录
前言
提示:
一、类的加载ClassLoader
1. 主动加载的方式有四种
- Student s = new Student();
- 反射,Clone
- 初始化子类的时候,父类也会被初始化
- 调用一个静态的方法
2.类加载的过程
-
加载Loader
- 通过类的全路径名,获取类的二进制的数据流
- 解析流,将类的信息存放于方法区中
- 创建这个Class类的实例
-
验证
验证加载的这个字节码是不是合法(格式,语义,符号引用等) -
准备
虚拟机为这个类分配相应的内存空间 -
解析
将符号引用转化为直接引用 -
初始化
到该阶段,表示该类已经成功被加载到了系统中,这时候类才会执行java字节码文件
二、JMM
1.程序计数器
- 具有一个较小的内存空间
- 正常的非native方法:看做是当前线程锁执行的字节码的行号指示器(分支,循环,跳转,异常处理和线程恢复等基础功能都需要依赖计数器来完成)
- native的方法: undefined
- 内存是私有的
- 唯一的一个没有规定任何OutOfMemoryError情况的区域
2.本地方法栈
- 主要是为了native方法服务的,与虚拟机栈功能类型
3.虚拟机栈
- 线程私有得
- 描述方法执行的内存模型
- 方法开始执行—> 结束执行。(对应的)栈帧的入栈---->出栈
- 存放的是方法本身和方法变量,比如int,short
4.方法区/永久代/元空间
- 共有的
- 方法区(逻辑概念)
- 逻辑上的概念
- 加载类的定义信息,常量,静态变量,方法数据,代码等…
- 永久代(JDK7以及以前)(方法区的一个实现)
- 内存中不会被GC的一块永久区域
- 加载了类信息和元数据的信息
- 如果空间被加载的类信息占满了,那么就会发生OOM的异常
- 元空间(JDK8之后)
- 永久代就被彻底的移除了。被元空间所代替
- 直接使用了本地的内存
- HopSpot,JRockit
5.堆
- 共有的
- 创建对象或者数组保存的地方(Java堆处于物理不连续的内存空间中,只要是逻辑上连续即可)
- GC发生的区域
- 新生代(Eden,S0,S1)和老年代
垃圾回收
GC ROOTS
- 那些可以被称为GC Roots作为根节点:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
作为GC Roots的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(栈帧中的本地变量)中,虚拟机,本地方法栈这都是局部变量,某个方法执行完毕之后,某些局部使用的对象可以被回收
1. 标记对象是否是可回收
- 可触及性:确定一个对象是不是可以被回收了
- 从根节点开始,访问某个对象,如果能够被访问到,说明该对象是可以使用的,反之,不能使用。
- 可触及 ----> 可复活 ----> 不可触及
public class DieAliveObject {
private static DieAliveObject dieAliveObject;
public static void main(String[] args) {
dieAliveObject = new DieAliveObject();
for (int i = 0; i < 1; i++) {
dieAliveObject = null;
System.gc();//通知JVM可以执行GC
try {
//等待GC执行
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
if (dieAliveObject == null) {
System.out.println("dieAliveObject 为空");
}else {
System.out.println("dieAliveObject 不为空");
}
}
}
/**
* 只会被调用一次,给对象唯一一次重生的机会
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
dieAliveObject = this; //使对象重生
}
}
如果没有显式的重写finalize()方法,是不会产生复活效果
2.引用级别
- 强引用
程序中的引用,Student s = new Student();
- 软引用
堆空间不足的时候才会被回收
- 弱引用
当GC发生的时候,只要发现是弱引用,无论空间是否足够,都会被回收
- 虚引用
和没有引用一样
3.槽位复用
4.对象分配
1. 栈上分配
- 逃逸技术
Student student = new Student();
public void G1(){
//Student student = new Student();
Gc1();
System.gc();
}
- 标量替换
- 标量
不可进一步分解的量。(主要是基本数据类型int,long)
- 聚合量
可以被进一步分解的量
- 替换
- 通过逃逸技术,确定这个对象不会被外部访问
- 会对这个对象进一步分解(若干个变量所替代。用标量去替代聚合量)
5.TLAB分配
TLAB:(Thread Local Allocation Buffer)线程本地分配缓存区
- 作用:避免多线程之间的冲突
TLAB:内存空间比较小(默认情况下,只占Eden的1%)。大的对象,是不能分配到这里的
6. 堆上分配
绝大多数对象的分配方式
总结
提示: